Struct Terminal

Source
pub struct Terminal { /* private fields */ }
Expand description

Creates a scrollable display widget to handle terminal-like behavior, such as logging events or debug information. Replaces SimpleTerminal widget

Implementations§

Source§

impl Terminal

Source

pub fn new<'a, T: Into<Option<&'a str>>>( x: i32, y: i32, width: i32, height: i32, title: T, ) -> Terminal

Creates a new widget, takes an x, y coordinates, as well as a width and height, plus a title

§Arguments
  • x - The x coordinate in the screen
  • y - The y coordinate in the screen
  • width - The width of the widget
  • heigth - The height of the widget
  • title - The title or label of the widget

To use dynamic strings use with_label(self, &str) or set_label(&mut self, &str). labels support special symbols preceded by an @ sign and for the associated formatting.

Examples found in repository?
examples/terminal.rs (line 44)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn default_fill() -> Self

Constructs a widget with the size of its parent

Source§

impl Terminal

Source

pub fn ansi(&self) -> bool

Returns whether the terminal is in ANSI mode.

Examples found in repository?
examples/terminal.rs (line 120)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
425//--------------------------------------------------------------------------------------
426/// More tests that run when the menu bar Test1 is clicked
427fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
428    term.take_focus().unwrap();
429    term.reset_terminal();
430    term.append("0123456789 0\n");
431    term.append("0123456789 1\n");
432    term.append("0123456789 2\n");
433    term.append("0123456789 3\n");
434    term.append("0123456789 4\n");
435    term.append("0123456789 5\n");
436    term.append("0123456789 6\n");
437    term.append("0123456789 7\n");
438    term.append("0123456789 8\n");
439    term.append("0123456789 9\n");
440    term.append("------------\n");
441
442    term.set_text_fg_color(Color::Green);
443    term.plot_char('A', 0, 0);
444    term.plot_char('B', 1, 1);
445    term.plot_char('C', 2, 2);
446    term.plot_char('D', 3, 3);
447    term.plot_char('E', 4, 4);
448    term.plot_char('F', 5, 5);
449    term.set_text_fg_color(Color::XtermWhite);
450
451    assert_eq!(term.cursor_row(), 11);
452    assert_eq!(term.cursor_col(), 0);
453
454    term.set_text_bg_color(Color::DarkBlue);
455    term.plot_char_utf8('b', 8, 1);
456    term.plot_char_utf8('↑', 9, 1);
457    term.plot_char_utf8('c', 8, 2);
458    term.plot_char_utf8('↑', 9, 2);
459    term.plot_char_utf8('d', 8, 3);
460    term.plot_char_utf8('↑', 9, 3);
461    term.plot_char_utf8('e', 8, 4);
462    term.plot_char_utf8('↑', 9, 4);
463    term.plot_char_utf8('f', 8, 5);
464    term.plot_char_utf8('↑', 9, 5);
465    term.plot_char_utf8('g', 8, 6);
466    term.plot_char_utf8('↑', 9, 6);
467    term.set_text_bg_color(Color::TransparentBg);
468
469    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
470    term.append("Done!\n");
471    term.set_text_attrib(Attrib::Normal);
472}
473
474//--------------------------------------------------------------------------------------
475/// More tests that run when the menu bar button Test2 is clicked
476fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
477    term.take_focus().unwrap();
478    term.reset_terminal();
479
480    for i in 0..50 {
481        term.append(&format!("{i}\n"));
482    }
483    assert_eq!(term.history_rows(), 100);
484
485    term.clear_history();
486    assert_eq!(term.history_use(), 0);
487
488    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
489    term.append("\nDone!\n");
490    term.set_text_attrib(Attrib::Normal);
491}
492
493//--------------------------------------------------------------------------------------
494/// Another set of tests that run when Test3 is clicked
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496    term.take_focus().unwrap();
497    term.reset_terminal();
498    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500    assert_eq!(term.history_use(), 0);
501    term.clear();
502    assert_eq!(term.cursor_row(), 0);
503    assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505    term.append("Test\ntext\na\nb\nc\nd");
506    assert_eq!(term.cursor_row(), 5);
507    let hist = term.history_use();
508    term.clear_screen_home(false);
509    assert_eq!(term.cursor_row(), 0);
510    assert_eq!(term.history_use(), hist); // History not changed
511
512    term.append("Test\ntext\na\nb\nc\nd\ne");
513    assert_eq!(term.cursor_row(), 6);
514    term.clear_screen_home(true);
515    assert_eq!(term.cursor_row(), 0);
516
517    term.append("Test\ntext\na\nb\nc\n");
518    assert_eq!(term.cursor_row(), 5);
519    term.clear_to_color(Color::DarkBlue);
520    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521    assert_eq!(term.text_bg_color(), Color::TransparentBg);
522    assert_eq!(term.cursor_row(), 0);
523
524    // Test cursor_home()
525    term.append("Test\n\n\n\n\n\n\n\n\n\n");
526    assert_eq!(term.cursor_row(), 10);
527    term.cursor_home();
528    assert_eq!(term.cursor_row(), 0);
529
530    // Test the widget color
531    assert_eq!(term.color(), Color::Black); // Default
532    term.set_color(Color::DarkGreen);
533    assert_eq!(term.color(), Color::DarkGreen);
534    term.set_color(Color::Black);
535    assert_eq!(term.color(), Color::Black);
536    term.append(
537        "This should be one line of white text on black, embedded into the top of a blue field.\n",
538    );
539
540    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541    term.set_output_translate(OutFlags::OFF);
542    assert_eq!(term.output_translate(), OutFlags::OFF);
543    term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547    term.append("\nDone!\n");
548    term.set_text_attrib(Attrib::Normal);
549}
550
551//--------------------------------------------------------------------------------------
552/// Another set of tests for the ring-buffer access methods
553/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555    let sel_len = term.selection_text_len();
556    let sel = term.selection_text();
557
558    term.take_focus().unwrap();
559    term.reset_terminal();
560    // Test the Utf8Char primitive
561    let uc = Utf8Char::new(b'Q');
562    let uc1 = uc.text_utf8();
563    assert_eq!(&uc1, b"Q");
564    assert_eq!(&uc.attrib(), &Attrib::Normal);
565    assert_eq!(
566        &uc.charflags(),
567        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568    );
569    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570    assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572    let ring_rows = term.ring_rows();
573
574    term.take_focus().unwrap();
575    term.clear_history();
576    assert_eq!(term.history_use(), 0);
577
578    // Subtract row numbers, modulo `rows`
579    fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580        match a - b {
581            n if n < 0 => n + rows,
582            n => n,
583        }
584    }
585    // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587    assert!(term.disp_srow() >= 0);
588    assert!(term.disp_erow() >= 0);
589    assert!(term.hist_srow() >= 0);
590    assert!(term.hist_erow() >= 0);
591    assert!(term.offset() >= 0);
592    assert!(term.disp_srow() <= ring_rows);
593    assert!(term.disp_erow() <= ring_rows);
594    assert!(term.hist_srow() <= ring_rows);
595    assert!(term.hist_erow() <= ring_rows);
596    assert!(term.offset() <= ring_rows);
597
598    assert_eq!(term.ring_srow(), 0);
599    assert_eq!(term.ring_erow(), ring_rows - 1);
600    assert_eq!(
601        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602        term.display_rows()
603    );
604    assert_eq!(
605        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606        term.history_rows()
607    );
608
609    assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610    assert_eq!(term.ring_srow(), 0);
611
612    /// Local function to read back all rows from the display into a long string.
613    /// Does not include scrollback history.
614    /// Trims trailing blanks on each line
615    fn read_disp(term: &Terminal) -> String {
616        let rows = term.display_rows();
617        let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618        for row in 0..rows {
619            let r = term.u8c_disp_row(row).trim();
620            // Iterate through a row, accumulating [u8]
621            for c in r.iter() {
622                // Note: Sometimes utf-8 length is > 1
623                text.extend_from_slice(c.text_utf8());
624            }
625            text.extend_from_slice(b"\n");
626        }
627        // Return the result as a string
628        std::str::from_utf8(&text).unwrap().to_string()
629    }
630
631    term.clear();
632    term.append("Top line  ↑ (up-arrow)");
633    term.set_text_attrib(Attrib::Underline);
634    term.append("  ");
635    term.set_text_attrib(Attrib::Normal);
636    term.append("  \n");
637    let mut text_out = read_disp(term);
638    // Trim trailing empty lines
639    text_out = text_out.trim_end_matches(&"\n").to_string();
640    // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642    assert_eq!(text_out, "Top line  ↑ (up-arrow)  ");
643    let r = term.u8c_disp_row(0);
644    assert_eq!(r.col(0).text_utf8(), b"T");
645    assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646    assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647    let r = term.u8c_disp_row(1);
648    assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649    assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651    // Clear the screen again, then append test text, then read it back and compare
652    let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656            Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659    term.clear_history();
660    term.clear();
661    let bg_save = term.text_bg_color();
662    let fg_save = term.text_fg_color();
663    term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664    term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665    term.append(test_text);
666    term.set_text_bg_color(bg_save);
667    term.set_text_fg_color(fg_save);
668
669    let mut text_out = read_disp(term);
670    // Trim trailing empty lines
671    text_out = text_out.trim_end_matches(&"\n").to_string();
672    assert_eq!(test_text, text_out);
673
674    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676    assert_eq!(term.ring_srow(), 0);
677    assert_eq!(term.ring_erow(), ring_rows - 1);
678    assert_eq!(
679        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680        term.display_rows()
681    );
682    assert_eq!(
683        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684        term.history_rows()
685    );
686
687    term.append(&format!(
688        "\n\nScreen has {} rows of {} columns.\n",
689        term.display_rows(),
690        term.display_columns()
691    ));
692
693    term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
695
696//--------------------------------------------------------------------------------------
697/// Yet another set of tests for misc cursor functions and other stuff
698/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700    term.take_focus().unwrap();
701
702    // Test the attr_fg_color and attr_bg_color methods.
703    // Put a single character 'A' into the buffer and check it
704    term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705    term.set_text_bg_color(Color::TransparentBg);
706    term.set_text_fg_color(Color::XtermWhite);
707    term.append("A");
708    let r = &term.u8c_disp_row(0);
709    let uc = r.col(0);
710    assert_eq!(uc.text_utf8(), b"A");
711    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712    assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713    assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714    assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715    assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717    // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718    term.clear();
719    term.set_text_fg_color_xterm(XtermColor::White);
720    term.set_text_bg_color_xterm(XtermColor::Black);
721    assert_eq!(term.text_attrib(), Attrib::Normal);
722
723    assert!(term.ansi());
724    term.append("B\x1b[32mC\x1b[1mD\n");
725
726    let r = &term.u8c_disp_row(0);
727    let uc = r.col(0);
728    assert_eq!(uc.text_utf8(), b"B");
729    assert!(uc.is_char(b'B'));
730    assert!(!uc.is_char(b'A'));
731    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735    assert_eq!(
736        &uc.charflags(),
737        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738    );
739
740    let uc = r.col(1);
741    assert_eq!(uc.text_utf8(), b"C");
742    assert!(uc.is_char(b'C'));
743    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747    assert_eq!(
748        &uc.charflags(),
749        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750    );
751
752    let uc = r.col(2);
753    assert_eq!(uc.text_utf8(), b"D");
754    assert!(uc.is_char(b'D'));
755    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757    assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759    assert_eq!(
760        &uc.attr_fgcolor(Some(term)),
761        &Color::from_rgb(0x20, 0xf0, 0x20)
762    );
763    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764    assert_eq!(
765        &uc.charflags(),
766        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767    );
768
769    // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770    term.clear();
771    term.set_text_fg_color_xterm(XtermColor::White);
772    term.set_text_bg_color_xterm(XtermColor::Black);
773    term.set_text_attrib(Attrib::Normal);
774    assert_eq!(term.text_attrib(), Attrib::Normal);
775
776    assert!(term.ansi());
777    term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779    let r = &term.u8c_disp_row(0);
780    let uc = r.col(0);
781    assert_eq!(uc.text_utf8(), b"B");
782    assert!(uc.is_char(b'B'));
783    assert!(!uc.is_char(b'A'));
784    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788    assert_eq!(
789        &uc.charflags(),
790        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791    );
792
793    let uc = r.col(1);
794    assert_eq!(uc.text_utf8(), b"C");
795    assert!(uc.is_char(b'C'));
796    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800    assert_eq!(
801        &uc.charflags(),
802        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803    );
804
805    let uc = r.col(2);
806    assert_eq!(uc.text_utf8(), b"D");
807    assert!(uc.is_char(b'D'));
808    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812    assert_eq!(
813        &uc.charflags(),
814        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815    );
816
817    let uc = r.col(3);
818    assert_eq!(uc.text_utf8(), b"E");
819    assert!(uc.is_char(b'E'));
820    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822    assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823    assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824    assert_eq!(
825        &uc.charflags(),
826        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827    );
828
829    // Test some miscellaneous Utf8 constants
830    assert_eq!(uc.length(), 1);
831    assert_eq!(uc.max_utf8(), 4);
832    assert_eq!(uc.pwidth(), 9.0);
833    assert_eq!(uc.pwidth_int(), 9);
834
835    term.set_text_fg_color_xterm(XtermColor::White);
836    term.set_text_bg_color_xterm(XtermColor::Black);
837    term.clear();
838    term.set_text_attrib(Attrib::Normal);
839
840    // Mouse selection functions
841    term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842    term.clear_mouse_selection();
843    assert_eq!(term.get_selection(), None);
844
845    // Play with cursor position
846    term.append("0123456789\n"); // Set up test pattern
847    term.append("ABCDEFGHIJ\n");
848    term.append("abcdefghij\n");
849
850    term.set_cursor_row(1);
851    assert_eq!(term.cursor_row(), 1);
852    term.set_cursor_col(1);
853    assert_eq!(term.cursor_col(), 1);
854    assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856    term.append("----"); // Overwrites text at cursor and moves cursor forward
857    assert_eq!(term.cursor_row(), 1);
858    assert_eq!(term.cursor_col(), 5);
859    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860    term.set_cursor_col(1);
861    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863    term.cursor_up(1, false);
864    assert_eq!(term.cursor_row(), 0);
865    assert_eq!(term.cursor_col(), 1);
866    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868    // Hit top of screen, so nothing happens
869    term.cursor_up(1, false);
870    assert_eq!(term.cursor_row(), 0);
871    assert_eq!(term.cursor_col(), 1);
872    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874    // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875    term.cursor_up(1, true);
876    assert_eq!(term.cursor_row(), 0);
877    assert_eq!(term.cursor_col(), 1);
878    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880    // Go back down to the overwritten text
881    term.cursor_down(2, false);
882    assert_eq!(term.cursor_row(), 2);
883    assert_eq!(term.cursor_col(), 1);
884    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886    // Go right past the overwritten text
887    term.cursor_right(4, false);
888    assert_eq!(term.cursor_row(), 2);
889    assert_eq!(term.cursor_col(), 5);
890    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892    // Go left to the end of the overwritten text
893    term.cursor_left(1);
894    assert_eq!(term.cursor_row(), 2);
895    assert_eq!(term.cursor_col(), 4);
896    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898    // Scroll back down, removing the blank line at the top.
899    // Cursor stays in place, the text moves under it.
900    term.scroll(1);
901    assert_eq!(term.cursor_row(), 2);
902    assert_eq!(term.cursor_col(), 4);
903    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905    // Clear from here to end-of-line
906    term.clear_eol();
907    assert_eq!(term.cursor_row(), 2);
908    assert_eq!(term.cursor_col(), 4);
909    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911    // Now clear from here to start-of-line. Cursor does not move.
912    term.clear_sol();
913    assert_eq!(term.cursor_row(), 2);
914    assert_eq!(term.cursor_col(), 4);
915    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916    term.cursor_left(1);
917    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918    term.set_cursor_col(0);
919    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921    // Clear some lines
922    term.clear_line(1);
923    assert_eq!(term.cursor_row(), 2);
924    assert_eq!(term.cursor_col(), 0);
925    term.set_cursor_row(1);
926    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927    term.set_cursor_row(3);
928    term.clear_cur_line();
929    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930    assert_eq!(term.cursor_row(), 3);
931    assert_eq!(term.cursor_col(), 0);
932
933    term.append("Two lines above are intentionally left blank.\n");
934    assert_eq!(term.cursor_row(), 4);
935    assert_eq!(term.cursor_col(), 0);
936
937    // Set up the test pattern again, then play with insert/delete
938    term.append("0123456789\n");
939    term.append("ABCDEFGHIJ\n");
940    term.append("abcdefghij\n");
941    assert_eq!(term.cursor_row(), 7);
942
943    term.set_cursor_row(4);
944    term.set_cursor_col(4);
945    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947    term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949    term.cursor_right(5, false);
950    assert_eq!(term.cursor_col(), 9);
951    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953    // Insert two blank rows above cursor. Cursor stays put.
954    term.insert_rows(2);
955    assert_eq!(term.cursor_row(), 4);
956    assert_eq!(term.cursor_col(), 9);
957    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958    term.cursor_down(2, false); // Go down to find our text again
959    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961    // Go back to the beginning of the inserted 'x' characters and delete them.
962    term.cursor_left(5);
963    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964    term.delete_cur_chars(5);
965    assert_eq!(term.cursor_row(), 6);
966    assert_eq!(term.cursor_col(), 4);
967    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969    term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970    term.cursor_down(1, false);
971    term.cursor_left(2);
972    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974    term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975    assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976    term.cursor_up(1, false);
977    term.delete_rows(2); // Delete remains of test pattern
978
979    term.set_text_attrib(Attrib::Bold);
980    term.insert_char_eol('-', 3, 15, 20);
981    term.set_cursor_row(3);
982    term.set_cursor_col(15);
983    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984    assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986    term.set_text_attrib(Attrib::Italic);
987    term.append(" and all lines below");
988    term.set_text_attrib(Attrib::Normal);
989    term.cursor_down(1, false);
990
991    let mut hsb = term.hscrollbar();
992    let mut sb = term.scrollbar();
993    hsb.set_value(100.0);
994    assert_eq!(hsb.value(), 100.0);
995    sb.set_value(50.0);
996    assert_eq!(sb.value(), 50.0);
997    hsb.set_value(0.0);
998    assert_eq!(hsb.value(), 0.0);
999    sb.set_value(0.0);
1000    assert_eq!(sb.value(), 0.0);
1001}
Source

pub fn set_ansi(&mut self, arg1: bool)

Enable/disable ANSI mode. If true, ANSI and VT100/xterm codes will be processed. If false, these codes won’t be processed and will either be ignored or print the error character “¿”, depending on the value of show_unknown().

Examples found in repository?
examples/terminal.rs (line 122)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn append(&mut self, s: &str)

Appends text to the terminal at current cursor position using the current text color/attributes. Redraws are managed automatically by default; see redraw_style()

Examples found in repository?
examples/terminal.rs (line 109)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
425//--------------------------------------------------------------------------------------
426/// More tests that run when the menu bar Test1 is clicked
427fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
428    term.take_focus().unwrap();
429    term.reset_terminal();
430    term.append("0123456789 0\n");
431    term.append("0123456789 1\n");
432    term.append("0123456789 2\n");
433    term.append("0123456789 3\n");
434    term.append("0123456789 4\n");
435    term.append("0123456789 5\n");
436    term.append("0123456789 6\n");
437    term.append("0123456789 7\n");
438    term.append("0123456789 8\n");
439    term.append("0123456789 9\n");
440    term.append("------------\n");
441
442    term.set_text_fg_color(Color::Green);
443    term.plot_char('A', 0, 0);
444    term.plot_char('B', 1, 1);
445    term.plot_char('C', 2, 2);
446    term.plot_char('D', 3, 3);
447    term.plot_char('E', 4, 4);
448    term.plot_char('F', 5, 5);
449    term.set_text_fg_color(Color::XtermWhite);
450
451    assert_eq!(term.cursor_row(), 11);
452    assert_eq!(term.cursor_col(), 0);
453
454    term.set_text_bg_color(Color::DarkBlue);
455    term.plot_char_utf8('b', 8, 1);
456    term.plot_char_utf8('↑', 9, 1);
457    term.plot_char_utf8('c', 8, 2);
458    term.plot_char_utf8('↑', 9, 2);
459    term.plot_char_utf8('d', 8, 3);
460    term.plot_char_utf8('↑', 9, 3);
461    term.plot_char_utf8('e', 8, 4);
462    term.plot_char_utf8('↑', 9, 4);
463    term.plot_char_utf8('f', 8, 5);
464    term.plot_char_utf8('↑', 9, 5);
465    term.plot_char_utf8('g', 8, 6);
466    term.plot_char_utf8('↑', 9, 6);
467    term.set_text_bg_color(Color::TransparentBg);
468
469    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
470    term.append("Done!\n");
471    term.set_text_attrib(Attrib::Normal);
472}
473
474//--------------------------------------------------------------------------------------
475/// More tests that run when the menu bar button Test2 is clicked
476fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
477    term.take_focus().unwrap();
478    term.reset_terminal();
479
480    for i in 0..50 {
481        term.append(&format!("{i}\n"));
482    }
483    assert_eq!(term.history_rows(), 100);
484
485    term.clear_history();
486    assert_eq!(term.history_use(), 0);
487
488    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
489    term.append("\nDone!\n");
490    term.set_text_attrib(Attrib::Normal);
491}
492
493//--------------------------------------------------------------------------------------
494/// Another set of tests that run when Test3 is clicked
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496    term.take_focus().unwrap();
497    term.reset_terminal();
498    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500    assert_eq!(term.history_use(), 0);
501    term.clear();
502    assert_eq!(term.cursor_row(), 0);
503    assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505    term.append("Test\ntext\na\nb\nc\nd");
506    assert_eq!(term.cursor_row(), 5);
507    let hist = term.history_use();
508    term.clear_screen_home(false);
509    assert_eq!(term.cursor_row(), 0);
510    assert_eq!(term.history_use(), hist); // History not changed
511
512    term.append("Test\ntext\na\nb\nc\nd\ne");
513    assert_eq!(term.cursor_row(), 6);
514    term.clear_screen_home(true);
515    assert_eq!(term.cursor_row(), 0);
516
517    term.append("Test\ntext\na\nb\nc\n");
518    assert_eq!(term.cursor_row(), 5);
519    term.clear_to_color(Color::DarkBlue);
520    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521    assert_eq!(term.text_bg_color(), Color::TransparentBg);
522    assert_eq!(term.cursor_row(), 0);
523
524    // Test cursor_home()
525    term.append("Test\n\n\n\n\n\n\n\n\n\n");
526    assert_eq!(term.cursor_row(), 10);
527    term.cursor_home();
528    assert_eq!(term.cursor_row(), 0);
529
530    // Test the widget color
531    assert_eq!(term.color(), Color::Black); // Default
532    term.set_color(Color::DarkGreen);
533    assert_eq!(term.color(), Color::DarkGreen);
534    term.set_color(Color::Black);
535    assert_eq!(term.color(), Color::Black);
536    term.append(
537        "This should be one line of white text on black, embedded into the top of a blue field.\n",
538    );
539
540    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541    term.set_output_translate(OutFlags::OFF);
542    assert_eq!(term.output_translate(), OutFlags::OFF);
543    term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547    term.append("\nDone!\n");
548    term.set_text_attrib(Attrib::Normal);
549}
550
551//--------------------------------------------------------------------------------------
552/// Another set of tests for the ring-buffer access methods
553/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555    let sel_len = term.selection_text_len();
556    let sel = term.selection_text();
557
558    term.take_focus().unwrap();
559    term.reset_terminal();
560    // Test the Utf8Char primitive
561    let uc = Utf8Char::new(b'Q');
562    let uc1 = uc.text_utf8();
563    assert_eq!(&uc1, b"Q");
564    assert_eq!(&uc.attrib(), &Attrib::Normal);
565    assert_eq!(
566        &uc.charflags(),
567        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568    );
569    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570    assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572    let ring_rows = term.ring_rows();
573
574    term.take_focus().unwrap();
575    term.clear_history();
576    assert_eq!(term.history_use(), 0);
577
578    // Subtract row numbers, modulo `rows`
579    fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580        match a - b {
581            n if n < 0 => n + rows,
582            n => n,
583        }
584    }
585    // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587    assert!(term.disp_srow() >= 0);
588    assert!(term.disp_erow() >= 0);
589    assert!(term.hist_srow() >= 0);
590    assert!(term.hist_erow() >= 0);
591    assert!(term.offset() >= 0);
592    assert!(term.disp_srow() <= ring_rows);
593    assert!(term.disp_erow() <= ring_rows);
594    assert!(term.hist_srow() <= ring_rows);
595    assert!(term.hist_erow() <= ring_rows);
596    assert!(term.offset() <= ring_rows);
597
598    assert_eq!(term.ring_srow(), 0);
599    assert_eq!(term.ring_erow(), ring_rows - 1);
600    assert_eq!(
601        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602        term.display_rows()
603    );
604    assert_eq!(
605        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606        term.history_rows()
607    );
608
609    assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610    assert_eq!(term.ring_srow(), 0);
611
612    /// Local function to read back all rows from the display into a long string.
613    /// Does not include scrollback history.
614    /// Trims trailing blanks on each line
615    fn read_disp(term: &Terminal) -> String {
616        let rows = term.display_rows();
617        let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618        for row in 0..rows {
619            let r = term.u8c_disp_row(row).trim();
620            // Iterate through a row, accumulating [u8]
621            for c in r.iter() {
622                // Note: Sometimes utf-8 length is > 1
623                text.extend_from_slice(c.text_utf8());
624            }
625            text.extend_from_slice(b"\n");
626        }
627        // Return the result as a string
628        std::str::from_utf8(&text).unwrap().to_string()
629    }
630
631    term.clear();
632    term.append("Top line  ↑ (up-arrow)");
633    term.set_text_attrib(Attrib::Underline);
634    term.append("  ");
635    term.set_text_attrib(Attrib::Normal);
636    term.append("  \n");
637    let mut text_out = read_disp(term);
638    // Trim trailing empty lines
639    text_out = text_out.trim_end_matches(&"\n").to_string();
640    // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642    assert_eq!(text_out, "Top line  ↑ (up-arrow)  ");
643    let r = term.u8c_disp_row(0);
644    assert_eq!(r.col(0).text_utf8(), b"T");
645    assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646    assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647    let r = term.u8c_disp_row(1);
648    assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649    assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651    // Clear the screen again, then append test text, then read it back and compare
652    let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656            Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659    term.clear_history();
660    term.clear();
661    let bg_save = term.text_bg_color();
662    let fg_save = term.text_fg_color();
663    term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664    term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665    term.append(test_text);
666    term.set_text_bg_color(bg_save);
667    term.set_text_fg_color(fg_save);
668
669    let mut text_out = read_disp(term);
670    // Trim trailing empty lines
671    text_out = text_out.trim_end_matches(&"\n").to_string();
672    assert_eq!(test_text, text_out);
673
674    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676    assert_eq!(term.ring_srow(), 0);
677    assert_eq!(term.ring_erow(), ring_rows - 1);
678    assert_eq!(
679        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680        term.display_rows()
681    );
682    assert_eq!(
683        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684        term.history_rows()
685    );
686
687    term.append(&format!(
688        "\n\nScreen has {} rows of {} columns.\n",
689        term.display_rows(),
690        term.display_columns()
691    ));
692
693    term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
695
696//--------------------------------------------------------------------------------------
697/// Yet another set of tests for misc cursor functions and other stuff
698/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700    term.take_focus().unwrap();
701
702    // Test the attr_fg_color and attr_bg_color methods.
703    // Put a single character 'A' into the buffer and check it
704    term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705    term.set_text_bg_color(Color::TransparentBg);
706    term.set_text_fg_color(Color::XtermWhite);
707    term.append("A");
708    let r = &term.u8c_disp_row(0);
709    let uc = r.col(0);
710    assert_eq!(uc.text_utf8(), b"A");
711    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712    assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713    assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714    assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715    assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717    // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718    term.clear();
719    term.set_text_fg_color_xterm(XtermColor::White);
720    term.set_text_bg_color_xterm(XtermColor::Black);
721    assert_eq!(term.text_attrib(), Attrib::Normal);
722
723    assert!(term.ansi());
724    term.append("B\x1b[32mC\x1b[1mD\n");
725
726    let r = &term.u8c_disp_row(0);
727    let uc = r.col(0);
728    assert_eq!(uc.text_utf8(), b"B");
729    assert!(uc.is_char(b'B'));
730    assert!(!uc.is_char(b'A'));
731    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735    assert_eq!(
736        &uc.charflags(),
737        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738    );
739
740    let uc = r.col(1);
741    assert_eq!(uc.text_utf8(), b"C");
742    assert!(uc.is_char(b'C'));
743    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747    assert_eq!(
748        &uc.charflags(),
749        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750    );
751
752    let uc = r.col(2);
753    assert_eq!(uc.text_utf8(), b"D");
754    assert!(uc.is_char(b'D'));
755    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757    assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759    assert_eq!(
760        &uc.attr_fgcolor(Some(term)),
761        &Color::from_rgb(0x20, 0xf0, 0x20)
762    );
763    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764    assert_eq!(
765        &uc.charflags(),
766        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767    );
768
769    // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770    term.clear();
771    term.set_text_fg_color_xterm(XtermColor::White);
772    term.set_text_bg_color_xterm(XtermColor::Black);
773    term.set_text_attrib(Attrib::Normal);
774    assert_eq!(term.text_attrib(), Attrib::Normal);
775
776    assert!(term.ansi());
777    term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779    let r = &term.u8c_disp_row(0);
780    let uc = r.col(0);
781    assert_eq!(uc.text_utf8(), b"B");
782    assert!(uc.is_char(b'B'));
783    assert!(!uc.is_char(b'A'));
784    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788    assert_eq!(
789        &uc.charflags(),
790        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791    );
792
793    let uc = r.col(1);
794    assert_eq!(uc.text_utf8(), b"C");
795    assert!(uc.is_char(b'C'));
796    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800    assert_eq!(
801        &uc.charflags(),
802        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803    );
804
805    let uc = r.col(2);
806    assert_eq!(uc.text_utf8(), b"D");
807    assert!(uc.is_char(b'D'));
808    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812    assert_eq!(
813        &uc.charflags(),
814        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815    );
816
817    let uc = r.col(3);
818    assert_eq!(uc.text_utf8(), b"E");
819    assert!(uc.is_char(b'E'));
820    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822    assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823    assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824    assert_eq!(
825        &uc.charflags(),
826        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827    );
828
829    // Test some miscellaneous Utf8 constants
830    assert_eq!(uc.length(), 1);
831    assert_eq!(uc.max_utf8(), 4);
832    assert_eq!(uc.pwidth(), 9.0);
833    assert_eq!(uc.pwidth_int(), 9);
834
835    term.set_text_fg_color_xterm(XtermColor::White);
836    term.set_text_bg_color_xterm(XtermColor::Black);
837    term.clear();
838    term.set_text_attrib(Attrib::Normal);
839
840    // Mouse selection functions
841    term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842    term.clear_mouse_selection();
843    assert_eq!(term.get_selection(), None);
844
845    // Play with cursor position
846    term.append("0123456789\n"); // Set up test pattern
847    term.append("ABCDEFGHIJ\n");
848    term.append("abcdefghij\n");
849
850    term.set_cursor_row(1);
851    assert_eq!(term.cursor_row(), 1);
852    term.set_cursor_col(1);
853    assert_eq!(term.cursor_col(), 1);
854    assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856    term.append("----"); // Overwrites text at cursor and moves cursor forward
857    assert_eq!(term.cursor_row(), 1);
858    assert_eq!(term.cursor_col(), 5);
859    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860    term.set_cursor_col(1);
861    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863    term.cursor_up(1, false);
864    assert_eq!(term.cursor_row(), 0);
865    assert_eq!(term.cursor_col(), 1);
866    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868    // Hit top of screen, so nothing happens
869    term.cursor_up(1, false);
870    assert_eq!(term.cursor_row(), 0);
871    assert_eq!(term.cursor_col(), 1);
872    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874    // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875    term.cursor_up(1, true);
876    assert_eq!(term.cursor_row(), 0);
877    assert_eq!(term.cursor_col(), 1);
878    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880    // Go back down to the overwritten text
881    term.cursor_down(2, false);
882    assert_eq!(term.cursor_row(), 2);
883    assert_eq!(term.cursor_col(), 1);
884    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886    // Go right past the overwritten text
887    term.cursor_right(4, false);
888    assert_eq!(term.cursor_row(), 2);
889    assert_eq!(term.cursor_col(), 5);
890    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892    // Go left to the end of the overwritten text
893    term.cursor_left(1);
894    assert_eq!(term.cursor_row(), 2);
895    assert_eq!(term.cursor_col(), 4);
896    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898    // Scroll back down, removing the blank line at the top.
899    // Cursor stays in place, the text moves under it.
900    term.scroll(1);
901    assert_eq!(term.cursor_row(), 2);
902    assert_eq!(term.cursor_col(), 4);
903    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905    // Clear from here to end-of-line
906    term.clear_eol();
907    assert_eq!(term.cursor_row(), 2);
908    assert_eq!(term.cursor_col(), 4);
909    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911    // Now clear from here to start-of-line. Cursor does not move.
912    term.clear_sol();
913    assert_eq!(term.cursor_row(), 2);
914    assert_eq!(term.cursor_col(), 4);
915    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916    term.cursor_left(1);
917    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918    term.set_cursor_col(0);
919    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921    // Clear some lines
922    term.clear_line(1);
923    assert_eq!(term.cursor_row(), 2);
924    assert_eq!(term.cursor_col(), 0);
925    term.set_cursor_row(1);
926    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927    term.set_cursor_row(3);
928    term.clear_cur_line();
929    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930    assert_eq!(term.cursor_row(), 3);
931    assert_eq!(term.cursor_col(), 0);
932
933    term.append("Two lines above are intentionally left blank.\n");
934    assert_eq!(term.cursor_row(), 4);
935    assert_eq!(term.cursor_col(), 0);
936
937    // Set up the test pattern again, then play with insert/delete
938    term.append("0123456789\n");
939    term.append("ABCDEFGHIJ\n");
940    term.append("abcdefghij\n");
941    assert_eq!(term.cursor_row(), 7);
942
943    term.set_cursor_row(4);
944    term.set_cursor_col(4);
945    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947    term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949    term.cursor_right(5, false);
950    assert_eq!(term.cursor_col(), 9);
951    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953    // Insert two blank rows above cursor. Cursor stays put.
954    term.insert_rows(2);
955    assert_eq!(term.cursor_row(), 4);
956    assert_eq!(term.cursor_col(), 9);
957    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958    term.cursor_down(2, false); // Go down to find our text again
959    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961    // Go back to the beginning of the inserted 'x' characters and delete them.
962    term.cursor_left(5);
963    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964    term.delete_cur_chars(5);
965    assert_eq!(term.cursor_row(), 6);
966    assert_eq!(term.cursor_col(), 4);
967    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969    term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970    term.cursor_down(1, false);
971    term.cursor_left(2);
972    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974    term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975    assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976    term.cursor_up(1, false);
977    term.delete_rows(2); // Delete remains of test pattern
978
979    term.set_text_attrib(Attrib::Bold);
980    term.insert_char_eol('-', 3, 15, 20);
981    term.set_cursor_row(3);
982    term.set_cursor_col(15);
983    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984    assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986    term.set_text_attrib(Attrib::Italic);
987    term.append(" and all lines below");
988    term.set_text_attrib(Attrib::Normal);
989    term.cursor_down(1, false);
990
991    let mut hsb = term.hscrollbar();
992    let mut sb = term.scrollbar();
993    hsb.set_value(100.0);
994    assert_eq!(hsb.value(), 100.0);
995    sb.set_value(50.0);
996    assert_eq!(sb.value(), 50.0);
997    hsb.set_value(0.0);
998    assert_eq!(hsb.value(), 0.0);
999    sb.set_value(0.0);
1000    assert_eq!(sb.value(), 0.0);
1001}
Source

pub fn append_u8(&mut self, s: &[u8])

Appends data to the terminal at current cursor position using the current text color/attributes Redraws are managed automatically by default; see redraw_style()

Examples found in repository?
examples/terminal.rs (line 126)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn append_ascii(&mut self, s: &str)

Appends text to the terminal at current cursor position using the current text color/attributes. Slightly more efficient than append_utf8 Redraws are managed automatically by default; see redraw_style()

Examples found in repository?
examples/terminal.rs (line 127)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn append_utf8(&mut self, s: &str)

Appends text to the terminal at current cursor position using the current text color/attributes. Handles UTF-8 chars split across calls Redraws are managed automatically by default; see redraw_style()

Examples found in repository?
examples/terminal.rs (line 144)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn append_utf8_u8(&mut self, s: &[u8])

Appends data to the terminal at current cursor position using the current text color/attributes Handles UTF-8 chars split across calls Redraws are managed automatically by default; see redraw_style()

Examples found in repository?
examples/terminal.rs (line 145)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn clear(&mut self)

Clears the screen to the current textbgcolor(), and homes the cursor.

Examples found in repository?
examples/terminal.rs (line 501)
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496    term.take_focus().unwrap();
497    term.reset_terminal();
498    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500    assert_eq!(term.history_use(), 0);
501    term.clear();
502    assert_eq!(term.cursor_row(), 0);
503    assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505    term.append("Test\ntext\na\nb\nc\nd");
506    assert_eq!(term.cursor_row(), 5);
507    let hist = term.history_use();
508    term.clear_screen_home(false);
509    assert_eq!(term.cursor_row(), 0);
510    assert_eq!(term.history_use(), hist); // History not changed
511
512    term.append("Test\ntext\na\nb\nc\nd\ne");
513    assert_eq!(term.cursor_row(), 6);
514    term.clear_screen_home(true);
515    assert_eq!(term.cursor_row(), 0);
516
517    term.append("Test\ntext\na\nb\nc\n");
518    assert_eq!(term.cursor_row(), 5);
519    term.clear_to_color(Color::DarkBlue);
520    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521    assert_eq!(term.text_bg_color(), Color::TransparentBg);
522    assert_eq!(term.cursor_row(), 0);
523
524    // Test cursor_home()
525    term.append("Test\n\n\n\n\n\n\n\n\n\n");
526    assert_eq!(term.cursor_row(), 10);
527    term.cursor_home();
528    assert_eq!(term.cursor_row(), 0);
529
530    // Test the widget color
531    assert_eq!(term.color(), Color::Black); // Default
532    term.set_color(Color::DarkGreen);
533    assert_eq!(term.color(), Color::DarkGreen);
534    term.set_color(Color::Black);
535    assert_eq!(term.color(), Color::Black);
536    term.append(
537        "This should be one line of white text on black, embedded into the top of a blue field.\n",
538    );
539
540    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541    term.set_output_translate(OutFlags::OFF);
542    assert_eq!(term.output_translate(), OutFlags::OFF);
543    term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547    term.append("\nDone!\n");
548    term.set_text_attrib(Attrib::Normal);
549}
550
551//--------------------------------------------------------------------------------------
552/// Another set of tests for the ring-buffer access methods
553/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555    let sel_len = term.selection_text_len();
556    let sel = term.selection_text();
557
558    term.take_focus().unwrap();
559    term.reset_terminal();
560    // Test the Utf8Char primitive
561    let uc = Utf8Char::new(b'Q');
562    let uc1 = uc.text_utf8();
563    assert_eq!(&uc1, b"Q");
564    assert_eq!(&uc.attrib(), &Attrib::Normal);
565    assert_eq!(
566        &uc.charflags(),
567        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568    );
569    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570    assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572    let ring_rows = term.ring_rows();
573
574    term.take_focus().unwrap();
575    term.clear_history();
576    assert_eq!(term.history_use(), 0);
577
578    // Subtract row numbers, modulo `rows`
579    fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580        match a - b {
581            n if n < 0 => n + rows,
582            n => n,
583        }
584    }
585    // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587    assert!(term.disp_srow() >= 0);
588    assert!(term.disp_erow() >= 0);
589    assert!(term.hist_srow() >= 0);
590    assert!(term.hist_erow() >= 0);
591    assert!(term.offset() >= 0);
592    assert!(term.disp_srow() <= ring_rows);
593    assert!(term.disp_erow() <= ring_rows);
594    assert!(term.hist_srow() <= ring_rows);
595    assert!(term.hist_erow() <= ring_rows);
596    assert!(term.offset() <= ring_rows);
597
598    assert_eq!(term.ring_srow(), 0);
599    assert_eq!(term.ring_erow(), ring_rows - 1);
600    assert_eq!(
601        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602        term.display_rows()
603    );
604    assert_eq!(
605        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606        term.history_rows()
607    );
608
609    assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610    assert_eq!(term.ring_srow(), 0);
611
612    /// Local function to read back all rows from the display into a long string.
613    /// Does not include scrollback history.
614    /// Trims trailing blanks on each line
615    fn read_disp(term: &Terminal) -> String {
616        let rows = term.display_rows();
617        let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618        for row in 0..rows {
619            let r = term.u8c_disp_row(row).trim();
620            // Iterate through a row, accumulating [u8]
621            for c in r.iter() {
622                // Note: Sometimes utf-8 length is > 1
623                text.extend_from_slice(c.text_utf8());
624            }
625            text.extend_from_slice(b"\n");
626        }
627        // Return the result as a string
628        std::str::from_utf8(&text).unwrap().to_string()
629    }
630
631    term.clear();
632    term.append("Top line  ↑ (up-arrow)");
633    term.set_text_attrib(Attrib::Underline);
634    term.append("  ");
635    term.set_text_attrib(Attrib::Normal);
636    term.append("  \n");
637    let mut text_out = read_disp(term);
638    // Trim trailing empty lines
639    text_out = text_out.trim_end_matches(&"\n").to_string();
640    // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642    assert_eq!(text_out, "Top line  ↑ (up-arrow)  ");
643    let r = term.u8c_disp_row(0);
644    assert_eq!(r.col(0).text_utf8(), b"T");
645    assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646    assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647    let r = term.u8c_disp_row(1);
648    assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649    assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651    // Clear the screen again, then append test text, then read it back and compare
652    let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656            Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659    term.clear_history();
660    term.clear();
661    let bg_save = term.text_bg_color();
662    let fg_save = term.text_fg_color();
663    term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664    term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665    term.append(test_text);
666    term.set_text_bg_color(bg_save);
667    term.set_text_fg_color(fg_save);
668
669    let mut text_out = read_disp(term);
670    // Trim trailing empty lines
671    text_out = text_out.trim_end_matches(&"\n").to_string();
672    assert_eq!(test_text, text_out);
673
674    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676    assert_eq!(term.ring_srow(), 0);
677    assert_eq!(term.ring_erow(), ring_rows - 1);
678    assert_eq!(
679        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680        term.display_rows()
681    );
682    assert_eq!(
683        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684        term.history_rows()
685    );
686
687    term.append(&format!(
688        "\n\nScreen has {} rows of {} columns.\n",
689        term.display_rows(),
690        term.display_columns()
691    ));
692
693    term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
695
696//--------------------------------------------------------------------------------------
697/// Yet another set of tests for misc cursor functions and other stuff
698/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700    term.take_focus().unwrap();
701
702    // Test the attr_fg_color and attr_bg_color methods.
703    // Put a single character 'A' into the buffer and check it
704    term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705    term.set_text_bg_color(Color::TransparentBg);
706    term.set_text_fg_color(Color::XtermWhite);
707    term.append("A");
708    let r = &term.u8c_disp_row(0);
709    let uc = r.col(0);
710    assert_eq!(uc.text_utf8(), b"A");
711    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712    assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713    assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714    assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715    assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717    // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718    term.clear();
719    term.set_text_fg_color_xterm(XtermColor::White);
720    term.set_text_bg_color_xterm(XtermColor::Black);
721    assert_eq!(term.text_attrib(), Attrib::Normal);
722
723    assert!(term.ansi());
724    term.append("B\x1b[32mC\x1b[1mD\n");
725
726    let r = &term.u8c_disp_row(0);
727    let uc = r.col(0);
728    assert_eq!(uc.text_utf8(), b"B");
729    assert!(uc.is_char(b'B'));
730    assert!(!uc.is_char(b'A'));
731    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735    assert_eq!(
736        &uc.charflags(),
737        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738    );
739
740    let uc = r.col(1);
741    assert_eq!(uc.text_utf8(), b"C");
742    assert!(uc.is_char(b'C'));
743    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747    assert_eq!(
748        &uc.charflags(),
749        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750    );
751
752    let uc = r.col(2);
753    assert_eq!(uc.text_utf8(), b"D");
754    assert!(uc.is_char(b'D'));
755    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757    assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759    assert_eq!(
760        &uc.attr_fgcolor(Some(term)),
761        &Color::from_rgb(0x20, 0xf0, 0x20)
762    );
763    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764    assert_eq!(
765        &uc.charflags(),
766        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767    );
768
769    // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770    term.clear();
771    term.set_text_fg_color_xterm(XtermColor::White);
772    term.set_text_bg_color_xterm(XtermColor::Black);
773    term.set_text_attrib(Attrib::Normal);
774    assert_eq!(term.text_attrib(), Attrib::Normal);
775
776    assert!(term.ansi());
777    term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779    let r = &term.u8c_disp_row(0);
780    let uc = r.col(0);
781    assert_eq!(uc.text_utf8(), b"B");
782    assert!(uc.is_char(b'B'));
783    assert!(!uc.is_char(b'A'));
784    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788    assert_eq!(
789        &uc.charflags(),
790        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791    );
792
793    let uc = r.col(1);
794    assert_eq!(uc.text_utf8(), b"C");
795    assert!(uc.is_char(b'C'));
796    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800    assert_eq!(
801        &uc.charflags(),
802        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803    );
804
805    let uc = r.col(2);
806    assert_eq!(uc.text_utf8(), b"D");
807    assert!(uc.is_char(b'D'));
808    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812    assert_eq!(
813        &uc.charflags(),
814        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815    );
816
817    let uc = r.col(3);
818    assert_eq!(uc.text_utf8(), b"E");
819    assert!(uc.is_char(b'E'));
820    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822    assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823    assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824    assert_eq!(
825        &uc.charflags(),
826        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827    );
828
829    // Test some miscellaneous Utf8 constants
830    assert_eq!(uc.length(), 1);
831    assert_eq!(uc.max_utf8(), 4);
832    assert_eq!(uc.pwidth(), 9.0);
833    assert_eq!(uc.pwidth_int(), 9);
834
835    term.set_text_fg_color_xterm(XtermColor::White);
836    term.set_text_bg_color_xterm(XtermColor::Black);
837    term.clear();
838    term.set_text_attrib(Attrib::Normal);
839
840    // Mouse selection functions
841    term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842    term.clear_mouse_selection();
843    assert_eq!(term.get_selection(), None);
844
845    // Play with cursor position
846    term.append("0123456789\n"); // Set up test pattern
847    term.append("ABCDEFGHIJ\n");
848    term.append("abcdefghij\n");
849
850    term.set_cursor_row(1);
851    assert_eq!(term.cursor_row(), 1);
852    term.set_cursor_col(1);
853    assert_eq!(term.cursor_col(), 1);
854    assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856    term.append("----"); // Overwrites text at cursor and moves cursor forward
857    assert_eq!(term.cursor_row(), 1);
858    assert_eq!(term.cursor_col(), 5);
859    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860    term.set_cursor_col(1);
861    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863    term.cursor_up(1, false);
864    assert_eq!(term.cursor_row(), 0);
865    assert_eq!(term.cursor_col(), 1);
866    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868    // Hit top of screen, so nothing happens
869    term.cursor_up(1, false);
870    assert_eq!(term.cursor_row(), 0);
871    assert_eq!(term.cursor_col(), 1);
872    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874    // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875    term.cursor_up(1, true);
876    assert_eq!(term.cursor_row(), 0);
877    assert_eq!(term.cursor_col(), 1);
878    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880    // Go back down to the overwritten text
881    term.cursor_down(2, false);
882    assert_eq!(term.cursor_row(), 2);
883    assert_eq!(term.cursor_col(), 1);
884    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886    // Go right past the overwritten text
887    term.cursor_right(4, false);
888    assert_eq!(term.cursor_row(), 2);
889    assert_eq!(term.cursor_col(), 5);
890    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892    // Go left to the end of the overwritten text
893    term.cursor_left(1);
894    assert_eq!(term.cursor_row(), 2);
895    assert_eq!(term.cursor_col(), 4);
896    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898    // Scroll back down, removing the blank line at the top.
899    // Cursor stays in place, the text moves under it.
900    term.scroll(1);
901    assert_eq!(term.cursor_row(), 2);
902    assert_eq!(term.cursor_col(), 4);
903    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905    // Clear from here to end-of-line
906    term.clear_eol();
907    assert_eq!(term.cursor_row(), 2);
908    assert_eq!(term.cursor_col(), 4);
909    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911    // Now clear from here to start-of-line. Cursor does not move.
912    term.clear_sol();
913    assert_eq!(term.cursor_row(), 2);
914    assert_eq!(term.cursor_col(), 4);
915    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916    term.cursor_left(1);
917    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918    term.set_cursor_col(0);
919    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921    // Clear some lines
922    term.clear_line(1);
923    assert_eq!(term.cursor_row(), 2);
924    assert_eq!(term.cursor_col(), 0);
925    term.set_cursor_row(1);
926    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927    term.set_cursor_row(3);
928    term.clear_cur_line();
929    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930    assert_eq!(term.cursor_row(), 3);
931    assert_eq!(term.cursor_col(), 0);
932
933    term.append("Two lines above are intentionally left blank.\n");
934    assert_eq!(term.cursor_row(), 4);
935    assert_eq!(term.cursor_col(), 0);
936
937    // Set up the test pattern again, then play with insert/delete
938    term.append("0123456789\n");
939    term.append("ABCDEFGHIJ\n");
940    term.append("abcdefghij\n");
941    assert_eq!(term.cursor_row(), 7);
942
943    term.set_cursor_row(4);
944    term.set_cursor_col(4);
945    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947    term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949    term.cursor_right(5, false);
950    assert_eq!(term.cursor_col(), 9);
951    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953    // Insert two blank rows above cursor. Cursor stays put.
954    term.insert_rows(2);
955    assert_eq!(term.cursor_row(), 4);
956    assert_eq!(term.cursor_col(), 9);
957    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958    term.cursor_down(2, false); // Go down to find our text again
959    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961    // Go back to the beginning of the inserted 'x' characters and delete them.
962    term.cursor_left(5);
963    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964    term.delete_cur_chars(5);
965    assert_eq!(term.cursor_row(), 6);
966    assert_eq!(term.cursor_col(), 4);
967    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969    term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970    term.cursor_down(1, false);
971    term.cursor_left(2);
972    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974    term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975    assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976    term.cursor_up(1, false);
977    term.delete_rows(2); // Delete remains of test pattern
978
979    term.set_text_attrib(Attrib::Bold);
980    term.insert_char_eol('-', 3, 15, 20);
981    term.set_cursor_row(3);
982    term.set_cursor_col(15);
983    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984    assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986    term.set_text_attrib(Attrib::Italic);
987    term.append(" and all lines below");
988    term.set_text_attrib(Attrib::Normal);
989    term.cursor_down(1, false);
990
991    let mut hsb = term.hscrollbar();
992    let mut sb = term.scrollbar();
993    hsb.set_value(100.0);
994    assert_eq!(hsb.value(), 100.0);
995    sb.set_value(50.0);
996    assert_eq!(sb.value(), 50.0);
997    hsb.set_value(0.0);
998    assert_eq!(hsb.value(), 0.0);
999    sb.set_value(0.0);
1000    assert_eq!(sb.value(), 0.0);
1001}
Source

pub fn clear_mouse_selection(&mut self)

Clear any current mouse selection.

Examples found in repository?
examples/terminal.rs (line 842)
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700    term.take_focus().unwrap();
701
702    // Test the attr_fg_color and attr_bg_color methods.
703    // Put a single character 'A' into the buffer and check it
704    term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705    term.set_text_bg_color(Color::TransparentBg);
706    term.set_text_fg_color(Color::XtermWhite);
707    term.append("A");
708    let r = &term.u8c_disp_row(0);
709    let uc = r.col(0);
710    assert_eq!(uc.text_utf8(), b"A");
711    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712    assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713    assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714    assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715    assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717    // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718    term.clear();
719    term.set_text_fg_color_xterm(XtermColor::White);
720    term.set_text_bg_color_xterm(XtermColor::Black);
721    assert_eq!(term.text_attrib(), Attrib::Normal);
722
723    assert!(term.ansi());
724    term.append("B\x1b[32mC\x1b[1mD\n");
725
726    let r = &term.u8c_disp_row(0);
727    let uc = r.col(0);
728    assert_eq!(uc.text_utf8(), b"B");
729    assert!(uc.is_char(b'B'));
730    assert!(!uc.is_char(b'A'));
731    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735    assert_eq!(
736        &uc.charflags(),
737        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738    );
739
740    let uc = r.col(1);
741    assert_eq!(uc.text_utf8(), b"C");
742    assert!(uc.is_char(b'C'));
743    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747    assert_eq!(
748        &uc.charflags(),
749        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750    );
751
752    let uc = r.col(2);
753    assert_eq!(uc.text_utf8(), b"D");
754    assert!(uc.is_char(b'D'));
755    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757    assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759    assert_eq!(
760        &uc.attr_fgcolor(Some(term)),
761        &Color::from_rgb(0x20, 0xf0, 0x20)
762    );
763    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764    assert_eq!(
765        &uc.charflags(),
766        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767    );
768
769    // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770    term.clear();
771    term.set_text_fg_color_xterm(XtermColor::White);
772    term.set_text_bg_color_xterm(XtermColor::Black);
773    term.set_text_attrib(Attrib::Normal);
774    assert_eq!(term.text_attrib(), Attrib::Normal);
775
776    assert!(term.ansi());
777    term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779    let r = &term.u8c_disp_row(0);
780    let uc = r.col(0);
781    assert_eq!(uc.text_utf8(), b"B");
782    assert!(uc.is_char(b'B'));
783    assert!(!uc.is_char(b'A'));
784    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788    assert_eq!(
789        &uc.charflags(),
790        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791    );
792
793    let uc = r.col(1);
794    assert_eq!(uc.text_utf8(), b"C");
795    assert!(uc.is_char(b'C'));
796    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800    assert_eq!(
801        &uc.charflags(),
802        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803    );
804
805    let uc = r.col(2);
806    assert_eq!(uc.text_utf8(), b"D");
807    assert!(uc.is_char(b'D'));
808    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812    assert_eq!(
813        &uc.charflags(),
814        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815    );
816
817    let uc = r.col(3);
818    assert_eq!(uc.text_utf8(), b"E");
819    assert!(uc.is_char(b'E'));
820    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822    assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823    assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824    assert_eq!(
825        &uc.charflags(),
826        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827    );
828
829    // Test some miscellaneous Utf8 constants
830    assert_eq!(uc.length(), 1);
831    assert_eq!(uc.max_utf8(), 4);
832    assert_eq!(uc.pwidth(), 9.0);
833    assert_eq!(uc.pwidth_int(), 9);
834
835    term.set_text_fg_color_xterm(XtermColor::White);
836    term.set_text_bg_color_xterm(XtermColor::Black);
837    term.clear();
838    term.set_text_attrib(Attrib::Normal);
839
840    // Mouse selection functions
841    term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842    term.clear_mouse_selection();
843    assert_eq!(term.get_selection(), None);
844
845    // Play with cursor position
846    term.append("0123456789\n"); // Set up test pattern
847    term.append("ABCDEFGHIJ\n");
848    term.append("abcdefghij\n");
849
850    term.set_cursor_row(1);
851    assert_eq!(term.cursor_row(), 1);
852    term.set_cursor_col(1);
853    assert_eq!(term.cursor_col(), 1);
854    assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856    term.append("----"); // Overwrites text at cursor and moves cursor forward
857    assert_eq!(term.cursor_row(), 1);
858    assert_eq!(term.cursor_col(), 5);
859    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860    term.set_cursor_col(1);
861    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863    term.cursor_up(1, false);
864    assert_eq!(term.cursor_row(), 0);
865    assert_eq!(term.cursor_col(), 1);
866    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868    // Hit top of screen, so nothing happens
869    term.cursor_up(1, false);
870    assert_eq!(term.cursor_row(), 0);
871    assert_eq!(term.cursor_col(), 1);
872    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874    // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875    term.cursor_up(1, true);
876    assert_eq!(term.cursor_row(), 0);
877    assert_eq!(term.cursor_col(), 1);
878    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880    // Go back down to the overwritten text
881    term.cursor_down(2, false);
882    assert_eq!(term.cursor_row(), 2);
883    assert_eq!(term.cursor_col(), 1);
884    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886    // Go right past the overwritten text
887    term.cursor_right(4, false);
888    assert_eq!(term.cursor_row(), 2);
889    assert_eq!(term.cursor_col(), 5);
890    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892    // Go left to the end of the overwritten text
893    term.cursor_left(1);
894    assert_eq!(term.cursor_row(), 2);
895    assert_eq!(term.cursor_col(), 4);
896    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898    // Scroll back down, removing the blank line at the top.
899    // Cursor stays in place, the text moves under it.
900    term.scroll(1);
901    assert_eq!(term.cursor_row(), 2);
902    assert_eq!(term.cursor_col(), 4);
903    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905    // Clear from here to end-of-line
906    term.clear_eol();
907    assert_eq!(term.cursor_row(), 2);
908    assert_eq!(term.cursor_col(), 4);
909    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911    // Now clear from here to start-of-line. Cursor does not move.
912    term.clear_sol();
913    assert_eq!(term.cursor_row(), 2);
914    assert_eq!(term.cursor_col(), 4);
915    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916    term.cursor_left(1);
917    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918    term.set_cursor_col(0);
919    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921    // Clear some lines
922    term.clear_line(1);
923    assert_eq!(term.cursor_row(), 2);
924    assert_eq!(term.cursor_col(), 0);
925    term.set_cursor_row(1);
926    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927    term.set_cursor_row(3);
928    term.clear_cur_line();
929    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930    assert_eq!(term.cursor_row(), 3);
931    assert_eq!(term.cursor_col(), 0);
932
933    term.append("Two lines above are intentionally left blank.\n");
934    assert_eq!(term.cursor_row(), 4);
935    assert_eq!(term.cursor_col(), 0);
936
937    // Set up the test pattern again, then play with insert/delete
938    term.append("0123456789\n");
939    term.append("ABCDEFGHIJ\n");
940    term.append("abcdefghij\n");
941    assert_eq!(term.cursor_row(), 7);
942
943    term.set_cursor_row(4);
944    term.set_cursor_col(4);
945    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947    term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949    term.cursor_right(5, false);
950    assert_eq!(term.cursor_col(), 9);
951    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953    // Insert two blank rows above cursor. Cursor stays put.
954    term.insert_rows(2);
955    assert_eq!(term.cursor_row(), 4);
956    assert_eq!(term.cursor_col(), 9);
957    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958    term.cursor_down(2, false); // Go down to find our text again
959    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961    // Go back to the beginning of the inserted 'x' characters and delete them.
962    term.cursor_left(5);
963    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964    term.delete_cur_chars(5);
965    assert_eq!(term.cursor_row(), 6);
966    assert_eq!(term.cursor_col(), 4);
967    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969    term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970    term.cursor_down(1, false);
971    term.cursor_left(2);
972    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974    term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975    assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976    term.cursor_up(1, false);
977    term.delete_rows(2); // Delete remains of test pattern
978
979    term.set_text_attrib(Attrib::Bold);
980    term.insert_char_eol('-', 3, 15, 20);
981    term.set_cursor_row(3);
982    term.set_cursor_col(15);
983    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984    assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986    term.set_text_attrib(Attrib::Italic);
987    term.append(" and all lines below");
988    term.set_text_attrib(Attrib::Normal);
989    term.cursor_down(1, false);
990
991    let mut hsb = term.hscrollbar();
992    let mut sb = term.scrollbar();
993    hsb.set_value(100.0);
994    assert_eq!(hsb.value(), 100.0);
995    sb.set_value(50.0);
996    assert_eq!(sb.value(), 50.0);
997    hsb.set_value(0.0);
998    assert_eq!(hsb.value(), 0.0);
999    sb.set_value(0.0);
1000    assert_eq!(sb.value(), 0.0);
1001}
Source

pub fn clear_to_color(&mut self, val: Color)

Clears the screen to a specific color val and homes the cursor. Does not affect the value of text_bg_color or text_bg_color_default

Examples found in repository?
examples/terminal.rs (line 519)
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496    term.take_focus().unwrap();
497    term.reset_terminal();
498    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500    assert_eq!(term.history_use(), 0);
501    term.clear();
502    assert_eq!(term.cursor_row(), 0);
503    assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505    term.append("Test\ntext\na\nb\nc\nd");
506    assert_eq!(term.cursor_row(), 5);
507    let hist = term.history_use();
508    term.clear_screen_home(false);
509    assert_eq!(term.cursor_row(), 0);
510    assert_eq!(term.history_use(), hist); // History not changed
511
512    term.append("Test\ntext\na\nb\nc\nd\ne");
513    assert_eq!(term.cursor_row(), 6);
514    term.clear_screen_home(true);
515    assert_eq!(term.cursor_row(), 0);
516
517    term.append("Test\ntext\na\nb\nc\n");
518    assert_eq!(term.cursor_row(), 5);
519    term.clear_to_color(Color::DarkBlue);
520    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521    assert_eq!(term.text_bg_color(), Color::TransparentBg);
522    assert_eq!(term.cursor_row(), 0);
523
524    // Test cursor_home()
525    term.append("Test\n\n\n\n\n\n\n\n\n\n");
526    assert_eq!(term.cursor_row(), 10);
527    term.cursor_home();
528    assert_eq!(term.cursor_row(), 0);
529
530    // Test the widget color
531    assert_eq!(term.color(), Color::Black); // Default
532    term.set_color(Color::DarkGreen);
533    assert_eq!(term.color(), Color::DarkGreen);
534    term.set_color(Color::Black);
535    assert_eq!(term.color(), Color::Black);
536    term.append(
537        "This should be one line of white text on black, embedded into the top of a blue field.\n",
538    );
539
540    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541    term.set_output_translate(OutFlags::OFF);
542    assert_eq!(term.output_translate(), OutFlags::OFF);
543    term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547    term.append("\nDone!\n");
548    term.set_text_attrib(Attrib::Normal);
549}
Source

pub fn clear_screen(&mut self, arg1: bool)

Clear the terminal screen only; does not affect the cursor position.

Also clears the current mouse selection.

If scroll_to_hist is true, the screen is cleared by scrolling the contents into the scrollback history, where it can be retrieved with the scrollbar. If false, the screen is cleared and the scrollback history is unchanged.

Similar to the escape sequence \<ESC\>[2J.

Source

pub fn clear_screen_home(&mut self, arg1: bool)

Clear the terminal screen and home the cursor

Also clears the current mouse selection.

If scroll_to_hist is true, the screen is cleared by scrolling the contents into the scrollback history, where it can be retrieved with the scrollbar. If false, the screen is cleared and the scrollback history is unchanged.

Similar to the escape sequence \<ESC\>[2J\<ESC\>[H.

Examples found in repository?
examples/terminal.rs (line 508)
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496    term.take_focus().unwrap();
497    term.reset_terminal();
498    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500    assert_eq!(term.history_use(), 0);
501    term.clear();
502    assert_eq!(term.cursor_row(), 0);
503    assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505    term.append("Test\ntext\na\nb\nc\nd");
506    assert_eq!(term.cursor_row(), 5);
507    let hist = term.history_use();
508    term.clear_screen_home(false);
509    assert_eq!(term.cursor_row(), 0);
510    assert_eq!(term.history_use(), hist); // History not changed
511
512    term.append("Test\ntext\na\nb\nc\nd\ne");
513    assert_eq!(term.cursor_row(), 6);
514    term.clear_screen_home(true);
515    assert_eq!(term.cursor_row(), 0);
516
517    term.append("Test\ntext\na\nb\nc\n");
518    assert_eq!(term.cursor_row(), 5);
519    term.clear_to_color(Color::DarkBlue);
520    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521    assert_eq!(term.text_bg_color(), Color::TransparentBg);
522    assert_eq!(term.cursor_row(), 0);
523
524    // Test cursor_home()
525    term.append("Test\n\n\n\n\n\n\n\n\n\n");
526    assert_eq!(term.cursor_row(), 10);
527    term.cursor_home();
528    assert_eq!(term.cursor_row(), 0);
529
530    // Test the widget color
531    assert_eq!(term.color(), Color::Black); // Default
532    term.set_color(Color::DarkGreen);
533    assert_eq!(term.color(), Color::DarkGreen);
534    term.set_color(Color::Black);
535    assert_eq!(term.color(), Color::Black);
536    term.append(
537        "This should be one line of white text on black, embedded into the top of a blue field.\n",
538    );
539
540    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541    term.set_output_translate(OutFlags::OFF);
542    assert_eq!(term.output_translate(), OutFlags::OFF);
543    term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547    term.append("\nDone!\n");
548    term.set_text_attrib(Attrib::Normal);
549}
Source

pub fn clear_history(&mut self)

Clears the scroll history buffer and adjusts scrollbar, forcing it to redraw()

Examples found in repository?
examples/terminal.rs (line 485)
476fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
477    term.take_focus().unwrap();
478    term.reset_terminal();
479
480    for i in 0..50 {
481        term.append(&format!("{i}\n"));
482    }
483    assert_eq!(term.history_rows(), 100);
484
485    term.clear_history();
486    assert_eq!(term.history_use(), 0);
487
488    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
489    term.append("\nDone!\n");
490    term.set_text_attrib(Attrib::Normal);
491}
492
493//--------------------------------------------------------------------------------------
494/// Another set of tests that run when Test3 is clicked
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496    term.take_focus().unwrap();
497    term.reset_terminal();
498    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500    assert_eq!(term.history_use(), 0);
501    term.clear();
502    assert_eq!(term.cursor_row(), 0);
503    assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505    term.append("Test\ntext\na\nb\nc\nd");
506    assert_eq!(term.cursor_row(), 5);
507    let hist = term.history_use();
508    term.clear_screen_home(false);
509    assert_eq!(term.cursor_row(), 0);
510    assert_eq!(term.history_use(), hist); // History not changed
511
512    term.append("Test\ntext\na\nb\nc\nd\ne");
513    assert_eq!(term.cursor_row(), 6);
514    term.clear_screen_home(true);
515    assert_eq!(term.cursor_row(), 0);
516
517    term.append("Test\ntext\na\nb\nc\n");
518    assert_eq!(term.cursor_row(), 5);
519    term.clear_to_color(Color::DarkBlue);
520    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521    assert_eq!(term.text_bg_color(), Color::TransparentBg);
522    assert_eq!(term.cursor_row(), 0);
523
524    // Test cursor_home()
525    term.append("Test\n\n\n\n\n\n\n\n\n\n");
526    assert_eq!(term.cursor_row(), 10);
527    term.cursor_home();
528    assert_eq!(term.cursor_row(), 0);
529
530    // Test the widget color
531    assert_eq!(term.color(), Color::Black); // Default
532    term.set_color(Color::DarkGreen);
533    assert_eq!(term.color(), Color::DarkGreen);
534    term.set_color(Color::Black);
535    assert_eq!(term.color(), Color::Black);
536    term.append(
537        "This should be one line of white text on black, embedded into the top of a blue field.\n",
538    );
539
540    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541    term.set_output_translate(OutFlags::OFF);
542    assert_eq!(term.output_translate(), OutFlags::OFF);
543    term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547    term.append("\nDone!\n");
548    term.set_text_attrib(Attrib::Normal);
549}
550
551//--------------------------------------------------------------------------------------
552/// Another set of tests for the ring-buffer access methods
553/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555    let sel_len = term.selection_text_len();
556    let sel = term.selection_text();
557
558    term.take_focus().unwrap();
559    term.reset_terminal();
560    // Test the Utf8Char primitive
561    let uc = Utf8Char::new(b'Q');
562    let uc1 = uc.text_utf8();
563    assert_eq!(&uc1, b"Q");
564    assert_eq!(&uc.attrib(), &Attrib::Normal);
565    assert_eq!(
566        &uc.charflags(),
567        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568    );
569    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570    assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572    let ring_rows = term.ring_rows();
573
574    term.take_focus().unwrap();
575    term.clear_history();
576    assert_eq!(term.history_use(), 0);
577
578    // Subtract row numbers, modulo `rows`
579    fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580        match a - b {
581            n if n < 0 => n + rows,
582            n => n,
583        }
584    }
585    // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587    assert!(term.disp_srow() >= 0);
588    assert!(term.disp_erow() >= 0);
589    assert!(term.hist_srow() >= 0);
590    assert!(term.hist_erow() >= 0);
591    assert!(term.offset() >= 0);
592    assert!(term.disp_srow() <= ring_rows);
593    assert!(term.disp_erow() <= ring_rows);
594    assert!(term.hist_srow() <= ring_rows);
595    assert!(term.hist_erow() <= ring_rows);
596    assert!(term.offset() <= ring_rows);
597
598    assert_eq!(term.ring_srow(), 0);
599    assert_eq!(term.ring_erow(), ring_rows - 1);
600    assert_eq!(
601        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602        term.display_rows()
603    );
604    assert_eq!(
605        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606        term.history_rows()
607    );
608
609    assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610    assert_eq!(term.ring_srow(), 0);
611
612    /// Local function to read back all rows from the display into a long string.
613    /// Does not include scrollback history.
614    /// Trims trailing blanks on each line
615    fn read_disp(term: &Terminal) -> String {
616        let rows = term.display_rows();
617        let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618        for row in 0..rows {
619            let r = term.u8c_disp_row(row).trim();
620            // Iterate through a row, accumulating [u8]
621            for c in r.iter() {
622                // Note: Sometimes utf-8 length is > 1
623                text.extend_from_slice(c.text_utf8());
624            }
625            text.extend_from_slice(b"\n");
626        }
627        // Return the result as a string
628        std::str::from_utf8(&text).unwrap().to_string()
629    }
630
631    term.clear();
632    term.append("Top line  ↑ (up-arrow)");
633    term.set_text_attrib(Attrib::Underline);
634    term.append("  ");
635    term.set_text_attrib(Attrib::Normal);
636    term.append("  \n");
637    let mut text_out = read_disp(term);
638    // Trim trailing empty lines
639    text_out = text_out.trim_end_matches(&"\n").to_string();
640    // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642    assert_eq!(text_out, "Top line  ↑ (up-arrow)  ");
643    let r = term.u8c_disp_row(0);
644    assert_eq!(r.col(0).text_utf8(), b"T");
645    assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646    assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647    let r = term.u8c_disp_row(1);
648    assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649    assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651    // Clear the screen again, then append test text, then read it back and compare
652    let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656            Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659    term.clear_history();
660    term.clear();
661    let bg_save = term.text_bg_color();
662    let fg_save = term.text_fg_color();
663    term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664    term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665    term.append(test_text);
666    term.set_text_bg_color(bg_save);
667    term.set_text_fg_color(fg_save);
668
669    let mut text_out = read_disp(term);
670    // Trim trailing empty lines
671    text_out = text_out.trim_end_matches(&"\n").to_string();
672    assert_eq!(test_text, text_out);
673
674    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676    assert_eq!(term.ring_srow(), 0);
677    assert_eq!(term.ring_erow(), ring_rows - 1);
678    assert_eq!(
679        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680        term.display_rows()
681    );
682    assert_eq!(
683        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684        term.history_rows()
685    );
686
687    term.append(&format!(
688        "\n\nScreen has {} rows of {} columns.\n",
689        term.display_rows(),
690        term.display_columns()
691    ));
692
693    term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
Source

pub fn color(&self) -> Color

Get the background color for the terminal’s Fl_Group::box().

Examples found in repository?
examples/terminal.rs (line 531)
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496    term.take_focus().unwrap();
497    term.reset_terminal();
498    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500    assert_eq!(term.history_use(), 0);
501    term.clear();
502    assert_eq!(term.cursor_row(), 0);
503    assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505    term.append("Test\ntext\na\nb\nc\nd");
506    assert_eq!(term.cursor_row(), 5);
507    let hist = term.history_use();
508    term.clear_screen_home(false);
509    assert_eq!(term.cursor_row(), 0);
510    assert_eq!(term.history_use(), hist); // History not changed
511
512    term.append("Test\ntext\na\nb\nc\nd\ne");
513    assert_eq!(term.cursor_row(), 6);
514    term.clear_screen_home(true);
515    assert_eq!(term.cursor_row(), 0);
516
517    term.append("Test\ntext\na\nb\nc\n");
518    assert_eq!(term.cursor_row(), 5);
519    term.clear_to_color(Color::DarkBlue);
520    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521    assert_eq!(term.text_bg_color(), Color::TransparentBg);
522    assert_eq!(term.cursor_row(), 0);
523
524    // Test cursor_home()
525    term.append("Test\n\n\n\n\n\n\n\n\n\n");
526    assert_eq!(term.cursor_row(), 10);
527    term.cursor_home();
528    assert_eq!(term.cursor_row(), 0);
529
530    // Test the widget color
531    assert_eq!(term.color(), Color::Black); // Default
532    term.set_color(Color::DarkGreen);
533    assert_eq!(term.color(), Color::DarkGreen);
534    term.set_color(Color::Black);
535    assert_eq!(term.color(), Color::Black);
536    term.append(
537        "This should be one line of white text on black, embedded into the top of a blue field.\n",
538    );
539
540    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541    term.set_output_translate(OutFlags::OFF);
542    assert_eq!(term.output_translate(), OutFlags::OFF);
543    term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547    term.append("\nDone!\n");
548    term.set_text_attrib(Attrib::Normal);
549}
Source

pub fn set_color(&mut self, color: Color)

Sets the background color for the terminal’s Fl_Group::box().

If the textbgcolor() and textbgcolor_default() are set to the special “see through” color 0xffffffff when any text was added, changing color() affects the color that shows through behind that existing text.

Otherwise, whatever specific background color was set for existing text will persist after changing color().

To see the effects of a change to color(), follow up with a call to redraw().

The default value is 0x0.

Examples found in repository?
examples/terminal.rs (line 532)
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496    term.take_focus().unwrap();
497    term.reset_terminal();
498    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500    assert_eq!(term.history_use(), 0);
501    term.clear();
502    assert_eq!(term.cursor_row(), 0);
503    assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505    term.append("Test\ntext\na\nb\nc\nd");
506    assert_eq!(term.cursor_row(), 5);
507    let hist = term.history_use();
508    term.clear_screen_home(false);
509    assert_eq!(term.cursor_row(), 0);
510    assert_eq!(term.history_use(), hist); // History not changed
511
512    term.append("Test\ntext\na\nb\nc\nd\ne");
513    assert_eq!(term.cursor_row(), 6);
514    term.clear_screen_home(true);
515    assert_eq!(term.cursor_row(), 0);
516
517    term.append("Test\ntext\na\nb\nc\n");
518    assert_eq!(term.cursor_row(), 5);
519    term.clear_to_color(Color::DarkBlue);
520    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521    assert_eq!(term.text_bg_color(), Color::TransparentBg);
522    assert_eq!(term.cursor_row(), 0);
523
524    // Test cursor_home()
525    term.append("Test\n\n\n\n\n\n\n\n\n\n");
526    assert_eq!(term.cursor_row(), 10);
527    term.cursor_home();
528    assert_eq!(term.cursor_row(), 0);
529
530    // Test the widget color
531    assert_eq!(term.color(), Color::Black); // Default
532    term.set_color(Color::DarkGreen);
533    assert_eq!(term.color(), Color::DarkGreen);
534    term.set_color(Color::Black);
535    assert_eq!(term.color(), Color::Black);
536    term.append(
537        "This should be one line of white text on black, embedded into the top of a blue field.\n",
538    );
539
540    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541    term.set_output_translate(OutFlags::OFF);
542    assert_eq!(term.output_translate(), OutFlags::OFF);
543    term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547    term.append("\nDone!\n");
548    term.set_text_attrib(Attrib::Normal);
549}
Source

pub fn cursor_col(&self) -> i32

Return the cursor’s current column position on the screen.

Examples found in repository?
examples/terminal.rs (line 148)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
425//--------------------------------------------------------------------------------------
426/// More tests that run when the menu bar Test1 is clicked
427fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
428    term.take_focus().unwrap();
429    term.reset_terminal();
430    term.append("0123456789 0\n");
431    term.append("0123456789 1\n");
432    term.append("0123456789 2\n");
433    term.append("0123456789 3\n");
434    term.append("0123456789 4\n");
435    term.append("0123456789 5\n");
436    term.append("0123456789 6\n");
437    term.append("0123456789 7\n");
438    term.append("0123456789 8\n");
439    term.append("0123456789 9\n");
440    term.append("------------\n");
441
442    term.set_text_fg_color(Color::Green);
443    term.plot_char('A', 0, 0);
444    term.plot_char('B', 1, 1);
445    term.plot_char('C', 2, 2);
446    term.plot_char('D', 3, 3);
447    term.plot_char('E', 4, 4);
448    term.plot_char('F', 5, 5);
449    term.set_text_fg_color(Color::XtermWhite);
450
451    assert_eq!(term.cursor_row(), 11);
452    assert_eq!(term.cursor_col(), 0);
453
454    term.set_text_bg_color(Color::DarkBlue);
455    term.plot_char_utf8('b', 8, 1);
456    term.plot_char_utf8('↑', 9, 1);
457    term.plot_char_utf8('c', 8, 2);
458    term.plot_char_utf8('↑', 9, 2);
459    term.plot_char_utf8('d', 8, 3);
460    term.plot_char_utf8('↑', 9, 3);
461    term.plot_char_utf8('e', 8, 4);
462    term.plot_char_utf8('↑', 9, 4);
463    term.plot_char_utf8('f', 8, 5);
464    term.plot_char_utf8('↑', 9, 5);
465    term.plot_char_utf8('g', 8, 6);
466    term.plot_char_utf8('↑', 9, 6);
467    term.set_text_bg_color(Color::TransparentBg);
468
469    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
470    term.append("Done!\n");
471    term.set_text_attrib(Attrib::Normal);
472}
473
474//--------------------------------------------------------------------------------------
475/// More tests that run when the menu bar button Test2 is clicked
476fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
477    term.take_focus().unwrap();
478    term.reset_terminal();
479
480    for i in 0..50 {
481        term.append(&format!("{i}\n"));
482    }
483    assert_eq!(term.history_rows(), 100);
484
485    term.clear_history();
486    assert_eq!(term.history_use(), 0);
487
488    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
489    term.append("\nDone!\n");
490    term.set_text_attrib(Attrib::Normal);
491}
492
493//--------------------------------------------------------------------------------------
494/// Another set of tests that run when Test3 is clicked
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496    term.take_focus().unwrap();
497    term.reset_terminal();
498    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500    assert_eq!(term.history_use(), 0);
501    term.clear();
502    assert_eq!(term.cursor_row(), 0);
503    assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505    term.append("Test\ntext\na\nb\nc\nd");
506    assert_eq!(term.cursor_row(), 5);
507    let hist = term.history_use();
508    term.clear_screen_home(false);
509    assert_eq!(term.cursor_row(), 0);
510    assert_eq!(term.history_use(), hist); // History not changed
511
512    term.append("Test\ntext\na\nb\nc\nd\ne");
513    assert_eq!(term.cursor_row(), 6);
514    term.clear_screen_home(true);
515    assert_eq!(term.cursor_row(), 0);
516
517    term.append("Test\ntext\na\nb\nc\n");
518    assert_eq!(term.cursor_row(), 5);
519    term.clear_to_color(Color::DarkBlue);
520    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521    assert_eq!(term.text_bg_color(), Color::TransparentBg);
522    assert_eq!(term.cursor_row(), 0);
523
524    // Test cursor_home()
525    term.append("Test\n\n\n\n\n\n\n\n\n\n");
526    assert_eq!(term.cursor_row(), 10);
527    term.cursor_home();
528    assert_eq!(term.cursor_row(), 0);
529
530    // Test the widget color
531    assert_eq!(term.color(), Color::Black); // Default
532    term.set_color(Color::DarkGreen);
533    assert_eq!(term.color(), Color::DarkGreen);
534    term.set_color(Color::Black);
535    assert_eq!(term.color(), Color::Black);
536    term.append(
537        "This should be one line of white text on black, embedded into the top of a blue field.\n",
538    );
539
540    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541    term.set_output_translate(OutFlags::OFF);
542    assert_eq!(term.output_translate(), OutFlags::OFF);
543    term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547    term.append("\nDone!\n");
548    term.set_text_attrib(Attrib::Normal);
549}
550
551//--------------------------------------------------------------------------------------
552/// Another set of tests for the ring-buffer access methods
553/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555    let sel_len = term.selection_text_len();
556    let sel = term.selection_text();
557
558    term.take_focus().unwrap();
559    term.reset_terminal();
560    // Test the Utf8Char primitive
561    let uc = Utf8Char::new(b'Q');
562    let uc1 = uc.text_utf8();
563    assert_eq!(&uc1, b"Q");
564    assert_eq!(&uc.attrib(), &Attrib::Normal);
565    assert_eq!(
566        &uc.charflags(),
567        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568    );
569    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570    assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572    let ring_rows = term.ring_rows();
573
574    term.take_focus().unwrap();
575    term.clear_history();
576    assert_eq!(term.history_use(), 0);
577
578    // Subtract row numbers, modulo `rows`
579    fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580        match a - b {
581            n if n < 0 => n + rows,
582            n => n,
583        }
584    }
585    // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587    assert!(term.disp_srow() >= 0);
588    assert!(term.disp_erow() >= 0);
589    assert!(term.hist_srow() >= 0);
590    assert!(term.hist_erow() >= 0);
591    assert!(term.offset() >= 0);
592    assert!(term.disp_srow() <= ring_rows);
593    assert!(term.disp_erow() <= ring_rows);
594    assert!(term.hist_srow() <= ring_rows);
595    assert!(term.hist_erow() <= ring_rows);
596    assert!(term.offset() <= ring_rows);
597
598    assert_eq!(term.ring_srow(), 0);
599    assert_eq!(term.ring_erow(), ring_rows - 1);
600    assert_eq!(
601        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602        term.display_rows()
603    );
604    assert_eq!(
605        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606        term.history_rows()
607    );
608
609    assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610    assert_eq!(term.ring_srow(), 0);
611
612    /// Local function to read back all rows from the display into a long string.
613    /// Does not include scrollback history.
614    /// Trims trailing blanks on each line
615    fn read_disp(term: &Terminal) -> String {
616        let rows = term.display_rows();
617        let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618        for row in 0..rows {
619            let r = term.u8c_disp_row(row).trim();
620            // Iterate through a row, accumulating [u8]
621            for c in r.iter() {
622                // Note: Sometimes utf-8 length is > 1
623                text.extend_from_slice(c.text_utf8());
624            }
625            text.extend_from_slice(b"\n");
626        }
627        // Return the result as a string
628        std::str::from_utf8(&text).unwrap().to_string()
629    }
630
631    term.clear();
632    term.append("Top line  ↑ (up-arrow)");
633    term.set_text_attrib(Attrib::Underline);
634    term.append("  ");
635    term.set_text_attrib(Attrib::Normal);
636    term.append("  \n");
637    let mut text_out = read_disp(term);
638    // Trim trailing empty lines
639    text_out = text_out.trim_end_matches(&"\n").to_string();
640    // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642    assert_eq!(text_out, "Top line  ↑ (up-arrow)  ");
643    let r = term.u8c_disp_row(0);
644    assert_eq!(r.col(0).text_utf8(), b"T");
645    assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646    assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647    let r = term.u8c_disp_row(1);
648    assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649    assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651    // Clear the screen again, then append test text, then read it back and compare
652    let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656            Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659    term.clear_history();
660    term.clear();
661    let bg_save = term.text_bg_color();
662    let fg_save = term.text_fg_color();
663    term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664    term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665    term.append(test_text);
666    term.set_text_bg_color(bg_save);
667    term.set_text_fg_color(fg_save);
668
669    let mut text_out = read_disp(term);
670    // Trim trailing empty lines
671    text_out = text_out.trim_end_matches(&"\n").to_string();
672    assert_eq!(test_text, text_out);
673
674    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676    assert_eq!(term.ring_srow(), 0);
677    assert_eq!(term.ring_erow(), ring_rows - 1);
678    assert_eq!(
679        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680        term.display_rows()
681    );
682    assert_eq!(
683        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684        term.history_rows()
685    );
686
687    term.append(&format!(
688        "\n\nScreen has {} rows of {} columns.\n",
689        term.display_rows(),
690        term.display_columns()
691    ));
692
693    term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
695
696//--------------------------------------------------------------------------------------
697/// Yet another set of tests for misc cursor functions and other stuff
698/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700    term.take_focus().unwrap();
701
702    // Test the attr_fg_color and attr_bg_color methods.
703    // Put a single character 'A' into the buffer and check it
704    term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705    term.set_text_bg_color(Color::TransparentBg);
706    term.set_text_fg_color(Color::XtermWhite);
707    term.append("A");
708    let r = &term.u8c_disp_row(0);
709    let uc = r.col(0);
710    assert_eq!(uc.text_utf8(), b"A");
711    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712    assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713    assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714    assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715    assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717    // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718    term.clear();
719    term.set_text_fg_color_xterm(XtermColor::White);
720    term.set_text_bg_color_xterm(XtermColor::Black);
721    assert_eq!(term.text_attrib(), Attrib::Normal);
722
723    assert!(term.ansi());
724    term.append("B\x1b[32mC\x1b[1mD\n");
725
726    let r = &term.u8c_disp_row(0);
727    let uc = r.col(0);
728    assert_eq!(uc.text_utf8(), b"B");
729    assert!(uc.is_char(b'B'));
730    assert!(!uc.is_char(b'A'));
731    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735    assert_eq!(
736        &uc.charflags(),
737        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738    );
739
740    let uc = r.col(1);
741    assert_eq!(uc.text_utf8(), b"C");
742    assert!(uc.is_char(b'C'));
743    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747    assert_eq!(
748        &uc.charflags(),
749        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750    );
751
752    let uc = r.col(2);
753    assert_eq!(uc.text_utf8(), b"D");
754    assert!(uc.is_char(b'D'));
755    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757    assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759    assert_eq!(
760        &uc.attr_fgcolor(Some(term)),
761        &Color::from_rgb(0x20, 0xf0, 0x20)
762    );
763    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764    assert_eq!(
765        &uc.charflags(),
766        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767    );
768
769    // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770    term.clear();
771    term.set_text_fg_color_xterm(XtermColor::White);
772    term.set_text_bg_color_xterm(XtermColor::Black);
773    term.set_text_attrib(Attrib::Normal);
774    assert_eq!(term.text_attrib(), Attrib::Normal);
775
776    assert!(term.ansi());
777    term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779    let r = &term.u8c_disp_row(0);
780    let uc = r.col(0);
781    assert_eq!(uc.text_utf8(), b"B");
782    assert!(uc.is_char(b'B'));
783    assert!(!uc.is_char(b'A'));
784    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788    assert_eq!(
789        &uc.charflags(),
790        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791    );
792
793    let uc = r.col(1);
794    assert_eq!(uc.text_utf8(), b"C");
795    assert!(uc.is_char(b'C'));
796    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800    assert_eq!(
801        &uc.charflags(),
802        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803    );
804
805    let uc = r.col(2);
806    assert_eq!(uc.text_utf8(), b"D");
807    assert!(uc.is_char(b'D'));
808    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812    assert_eq!(
813        &uc.charflags(),
814        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815    );
816
817    let uc = r.col(3);
818    assert_eq!(uc.text_utf8(), b"E");
819    assert!(uc.is_char(b'E'));
820    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822    assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823    assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824    assert_eq!(
825        &uc.charflags(),
826        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827    );
828
829    // Test some miscellaneous Utf8 constants
830    assert_eq!(uc.length(), 1);
831    assert_eq!(uc.max_utf8(), 4);
832    assert_eq!(uc.pwidth(), 9.0);
833    assert_eq!(uc.pwidth_int(), 9);
834
835    term.set_text_fg_color_xterm(XtermColor::White);
836    term.set_text_bg_color_xterm(XtermColor::Black);
837    term.clear();
838    term.set_text_attrib(Attrib::Normal);
839
840    // Mouse selection functions
841    term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842    term.clear_mouse_selection();
843    assert_eq!(term.get_selection(), None);
844
845    // Play with cursor position
846    term.append("0123456789\n"); // Set up test pattern
847    term.append("ABCDEFGHIJ\n");
848    term.append("abcdefghij\n");
849
850    term.set_cursor_row(1);
851    assert_eq!(term.cursor_row(), 1);
852    term.set_cursor_col(1);
853    assert_eq!(term.cursor_col(), 1);
854    assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856    term.append("----"); // Overwrites text at cursor and moves cursor forward
857    assert_eq!(term.cursor_row(), 1);
858    assert_eq!(term.cursor_col(), 5);
859    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860    term.set_cursor_col(1);
861    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863    term.cursor_up(1, false);
864    assert_eq!(term.cursor_row(), 0);
865    assert_eq!(term.cursor_col(), 1);
866    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868    // Hit top of screen, so nothing happens
869    term.cursor_up(1, false);
870    assert_eq!(term.cursor_row(), 0);
871    assert_eq!(term.cursor_col(), 1);
872    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874    // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875    term.cursor_up(1, true);
876    assert_eq!(term.cursor_row(), 0);
877    assert_eq!(term.cursor_col(), 1);
878    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880    // Go back down to the overwritten text
881    term.cursor_down(2, false);
882    assert_eq!(term.cursor_row(), 2);
883    assert_eq!(term.cursor_col(), 1);
884    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886    // Go right past the overwritten text
887    term.cursor_right(4, false);
888    assert_eq!(term.cursor_row(), 2);
889    assert_eq!(term.cursor_col(), 5);
890    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892    // Go left to the end of the overwritten text
893    term.cursor_left(1);
894    assert_eq!(term.cursor_row(), 2);
895    assert_eq!(term.cursor_col(), 4);
896    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898    // Scroll back down, removing the blank line at the top.
899    // Cursor stays in place, the text moves under it.
900    term.scroll(1);
901    assert_eq!(term.cursor_row(), 2);
902    assert_eq!(term.cursor_col(), 4);
903    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905    // Clear from here to end-of-line
906    term.clear_eol();
907    assert_eq!(term.cursor_row(), 2);
908    assert_eq!(term.cursor_col(), 4);
909    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911    // Now clear from here to start-of-line. Cursor does not move.
912    term.clear_sol();
913    assert_eq!(term.cursor_row(), 2);
914    assert_eq!(term.cursor_col(), 4);
915    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916    term.cursor_left(1);
917    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918    term.set_cursor_col(0);
919    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921    // Clear some lines
922    term.clear_line(1);
923    assert_eq!(term.cursor_row(), 2);
924    assert_eq!(term.cursor_col(), 0);
925    term.set_cursor_row(1);
926    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927    term.set_cursor_row(3);
928    term.clear_cur_line();
929    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930    assert_eq!(term.cursor_row(), 3);
931    assert_eq!(term.cursor_col(), 0);
932
933    term.append("Two lines above are intentionally left blank.\n");
934    assert_eq!(term.cursor_row(), 4);
935    assert_eq!(term.cursor_col(), 0);
936
937    // Set up the test pattern again, then play with insert/delete
938    term.append("0123456789\n");
939    term.append("ABCDEFGHIJ\n");
940    term.append("abcdefghij\n");
941    assert_eq!(term.cursor_row(), 7);
942
943    term.set_cursor_row(4);
944    term.set_cursor_col(4);
945    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947    term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949    term.cursor_right(5, false);
950    assert_eq!(term.cursor_col(), 9);
951    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953    // Insert two blank rows above cursor. Cursor stays put.
954    term.insert_rows(2);
955    assert_eq!(term.cursor_row(), 4);
956    assert_eq!(term.cursor_col(), 9);
957    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958    term.cursor_down(2, false); // Go down to find our text again
959    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961    // Go back to the beginning of the inserted 'x' characters and delete them.
962    term.cursor_left(5);
963    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964    term.delete_cur_chars(5);
965    assert_eq!(term.cursor_row(), 6);
966    assert_eq!(term.cursor_col(), 4);
967    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969    term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970    term.cursor_down(1, false);
971    term.cursor_left(2);
972    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974    term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975    assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976    term.cursor_up(1, false);
977    term.delete_rows(2); // Delete remains of test pattern
978
979    term.set_text_attrib(Attrib::Bold);
980    term.insert_char_eol('-', 3, 15, 20);
981    term.set_cursor_row(3);
982    term.set_cursor_col(15);
983    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984    assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986    term.set_text_attrib(Attrib::Italic);
987    term.append(" and all lines below");
988    term.set_text_attrib(Attrib::Normal);
989    term.cursor_down(1, false);
990
991    let mut hsb = term.hscrollbar();
992    let mut sb = term.scrollbar();
993    hsb.set_value(100.0);
994    assert_eq!(hsb.value(), 100.0);
995    sb.set_value(50.0);
996    assert_eq!(sb.value(), 50.0);
997    hsb.set_value(0.0);
998    assert_eq!(hsb.value(), 0.0);
999    sb.set_value(0.0);
1000    assert_eq!(sb.value(), 0.0);
1001}
Source

pub fn set_cursor_col(&mut self, val: i32)

Set the cursor’s current column position on the screen. This is a low-level “protected” function of the fltk library

Examples found in repository?
examples/terminal.rs (line 852)
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700    term.take_focus().unwrap();
701
702    // Test the attr_fg_color and attr_bg_color methods.
703    // Put a single character 'A' into the buffer and check it
704    term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705    term.set_text_bg_color(Color::TransparentBg);
706    term.set_text_fg_color(Color::XtermWhite);
707    term.append("A");
708    let r = &term.u8c_disp_row(0);
709    let uc = r.col(0);
710    assert_eq!(uc.text_utf8(), b"A");
711    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712    assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713    assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714    assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715    assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717    // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718    term.clear();
719    term.set_text_fg_color_xterm(XtermColor::White);
720    term.set_text_bg_color_xterm(XtermColor::Black);
721    assert_eq!(term.text_attrib(), Attrib::Normal);
722
723    assert!(term.ansi());
724    term.append("B\x1b[32mC\x1b[1mD\n");
725
726    let r = &term.u8c_disp_row(0);
727    let uc = r.col(0);
728    assert_eq!(uc.text_utf8(), b"B");
729    assert!(uc.is_char(b'B'));
730    assert!(!uc.is_char(b'A'));
731    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735    assert_eq!(
736        &uc.charflags(),
737        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738    );
739
740    let uc = r.col(1);
741    assert_eq!(uc.text_utf8(), b"C");
742    assert!(uc.is_char(b'C'));
743    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747    assert_eq!(
748        &uc.charflags(),
749        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750    );
751
752    let uc = r.col(2);
753    assert_eq!(uc.text_utf8(), b"D");
754    assert!(uc.is_char(b'D'));
755    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757    assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759    assert_eq!(
760        &uc.attr_fgcolor(Some(term)),
761        &Color::from_rgb(0x20, 0xf0, 0x20)
762    );
763    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764    assert_eq!(
765        &uc.charflags(),
766        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767    );
768
769    // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770    term.clear();
771    term.set_text_fg_color_xterm(XtermColor::White);
772    term.set_text_bg_color_xterm(XtermColor::Black);
773    term.set_text_attrib(Attrib::Normal);
774    assert_eq!(term.text_attrib(), Attrib::Normal);
775
776    assert!(term.ansi());
777    term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779    let r = &term.u8c_disp_row(0);
780    let uc = r.col(0);
781    assert_eq!(uc.text_utf8(), b"B");
782    assert!(uc.is_char(b'B'));
783    assert!(!uc.is_char(b'A'));
784    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788    assert_eq!(
789        &uc.charflags(),
790        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791    );
792
793    let uc = r.col(1);
794    assert_eq!(uc.text_utf8(), b"C");
795    assert!(uc.is_char(b'C'));
796    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800    assert_eq!(
801        &uc.charflags(),
802        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803    );
804
805    let uc = r.col(2);
806    assert_eq!(uc.text_utf8(), b"D");
807    assert!(uc.is_char(b'D'));
808    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812    assert_eq!(
813        &uc.charflags(),
814        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815    );
816
817    let uc = r.col(3);
818    assert_eq!(uc.text_utf8(), b"E");
819    assert!(uc.is_char(b'E'));
820    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822    assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823    assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824    assert_eq!(
825        &uc.charflags(),
826        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827    );
828
829    // Test some miscellaneous Utf8 constants
830    assert_eq!(uc.length(), 1);
831    assert_eq!(uc.max_utf8(), 4);
832    assert_eq!(uc.pwidth(), 9.0);
833    assert_eq!(uc.pwidth_int(), 9);
834
835    term.set_text_fg_color_xterm(XtermColor::White);
836    term.set_text_bg_color_xterm(XtermColor::Black);
837    term.clear();
838    term.set_text_attrib(Attrib::Normal);
839
840    // Mouse selection functions
841    term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842    term.clear_mouse_selection();
843    assert_eq!(term.get_selection(), None);
844
845    // Play with cursor position
846    term.append("0123456789\n"); // Set up test pattern
847    term.append("ABCDEFGHIJ\n");
848    term.append("abcdefghij\n");
849
850    term.set_cursor_row(1);
851    assert_eq!(term.cursor_row(), 1);
852    term.set_cursor_col(1);
853    assert_eq!(term.cursor_col(), 1);
854    assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856    term.append("----"); // Overwrites text at cursor and moves cursor forward
857    assert_eq!(term.cursor_row(), 1);
858    assert_eq!(term.cursor_col(), 5);
859    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860    term.set_cursor_col(1);
861    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863    term.cursor_up(1, false);
864    assert_eq!(term.cursor_row(), 0);
865    assert_eq!(term.cursor_col(), 1);
866    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868    // Hit top of screen, so nothing happens
869    term.cursor_up(1, false);
870    assert_eq!(term.cursor_row(), 0);
871    assert_eq!(term.cursor_col(), 1);
872    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874    // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875    term.cursor_up(1, true);
876    assert_eq!(term.cursor_row(), 0);
877    assert_eq!(term.cursor_col(), 1);
878    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880    // Go back down to the overwritten text
881    term.cursor_down(2, false);
882    assert_eq!(term.cursor_row(), 2);
883    assert_eq!(term.cursor_col(), 1);
884    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886    // Go right past the overwritten text
887    term.cursor_right(4, false);
888    assert_eq!(term.cursor_row(), 2);
889    assert_eq!(term.cursor_col(), 5);
890    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892    // Go left to the end of the overwritten text
893    term.cursor_left(1);
894    assert_eq!(term.cursor_row(), 2);
895    assert_eq!(term.cursor_col(), 4);
896    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898    // Scroll back down, removing the blank line at the top.
899    // Cursor stays in place, the text moves under it.
900    term.scroll(1);
901    assert_eq!(term.cursor_row(), 2);
902    assert_eq!(term.cursor_col(), 4);
903    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905    // Clear from here to end-of-line
906    term.clear_eol();
907    assert_eq!(term.cursor_row(), 2);
908    assert_eq!(term.cursor_col(), 4);
909    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911    // Now clear from here to start-of-line. Cursor does not move.
912    term.clear_sol();
913    assert_eq!(term.cursor_row(), 2);
914    assert_eq!(term.cursor_col(), 4);
915    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916    term.cursor_left(1);
917    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918    term.set_cursor_col(0);
919    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921    // Clear some lines
922    term.clear_line(1);
923    assert_eq!(term.cursor_row(), 2);
924    assert_eq!(term.cursor_col(), 0);
925    term.set_cursor_row(1);
926    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927    term.set_cursor_row(3);
928    term.clear_cur_line();
929    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930    assert_eq!(term.cursor_row(), 3);
931    assert_eq!(term.cursor_col(), 0);
932
933    term.append("Two lines above are intentionally left blank.\n");
934    assert_eq!(term.cursor_row(), 4);
935    assert_eq!(term.cursor_col(), 0);
936
937    // Set up the test pattern again, then play with insert/delete
938    term.append("0123456789\n");
939    term.append("ABCDEFGHIJ\n");
940    term.append("abcdefghij\n");
941    assert_eq!(term.cursor_row(), 7);
942
943    term.set_cursor_row(4);
944    term.set_cursor_col(4);
945    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947    term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949    term.cursor_right(5, false);
950    assert_eq!(term.cursor_col(), 9);
951    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953    // Insert two blank rows above cursor. Cursor stays put.
954    term.insert_rows(2);
955    assert_eq!(term.cursor_row(), 4);
956    assert_eq!(term.cursor_col(), 9);
957    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958    term.cursor_down(2, false); // Go down to find our text again
959    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961    // Go back to the beginning of the inserted 'x' characters and delete them.
962    term.cursor_left(5);
963    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964    term.delete_cur_chars(5);
965    assert_eq!(term.cursor_row(), 6);
966    assert_eq!(term.cursor_col(), 4);
967    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969    term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970    term.cursor_down(1, false);
971    term.cursor_left(2);
972    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974    term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975    assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976    term.cursor_up(1, false);
977    term.delete_rows(2); // Delete remains of test pattern
978
979    term.set_text_attrib(Attrib::Bold);
980    term.insert_char_eol('-', 3, 15, 20);
981    term.set_cursor_row(3);
982    term.set_cursor_col(15);
983    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984    assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986    term.set_text_attrib(Attrib::Italic);
987    term.append(" and all lines below");
988    term.set_text_attrib(Attrib::Normal);
989    term.cursor_down(1, false);
990
991    let mut hsb = term.hscrollbar();
992    let mut sb = term.scrollbar();
993    hsb.set_value(100.0);
994    assert_eq!(hsb.value(), 100.0);
995    sb.set_value(50.0);
996    assert_eq!(sb.value(), 50.0);
997    hsb.set_value(0.0);
998    assert_eq!(hsb.value(), 0.0);
999    sb.set_value(0.0);
1000    assert_eq!(sb.value(), 0.0);
1001}
Source

pub fn cursor_row(&self) -> i32

Return the cursor’s current row position on the screen.

Examples found in repository?
examples/terminal.rs (line 147)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
425//--------------------------------------------------------------------------------------
426/// More tests that run when the menu bar Test1 is clicked
427fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
428    term.take_focus().unwrap();
429    term.reset_terminal();
430    term.append("0123456789 0\n");
431    term.append("0123456789 1\n");
432    term.append("0123456789 2\n");
433    term.append("0123456789 3\n");
434    term.append("0123456789 4\n");
435    term.append("0123456789 5\n");
436    term.append("0123456789 6\n");
437    term.append("0123456789 7\n");
438    term.append("0123456789 8\n");
439    term.append("0123456789 9\n");
440    term.append("------------\n");
441
442    term.set_text_fg_color(Color::Green);
443    term.plot_char('A', 0, 0);
444    term.plot_char('B', 1, 1);
445    term.plot_char('C', 2, 2);
446    term.plot_char('D', 3, 3);
447    term.plot_char('E', 4, 4);
448    term.plot_char('F', 5, 5);
449    term.set_text_fg_color(Color::XtermWhite);
450
451    assert_eq!(term.cursor_row(), 11);
452    assert_eq!(term.cursor_col(), 0);
453
454    term.set_text_bg_color(Color::DarkBlue);
455    term.plot_char_utf8('b', 8, 1);
456    term.plot_char_utf8('↑', 9, 1);
457    term.plot_char_utf8('c', 8, 2);
458    term.plot_char_utf8('↑', 9, 2);
459    term.plot_char_utf8('d', 8, 3);
460    term.plot_char_utf8('↑', 9, 3);
461    term.plot_char_utf8('e', 8, 4);
462    term.plot_char_utf8('↑', 9, 4);
463    term.plot_char_utf8('f', 8, 5);
464    term.plot_char_utf8('↑', 9, 5);
465    term.plot_char_utf8('g', 8, 6);
466    term.plot_char_utf8('↑', 9, 6);
467    term.set_text_bg_color(Color::TransparentBg);
468
469    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
470    term.append("Done!\n");
471    term.set_text_attrib(Attrib::Normal);
472}
473
474//--------------------------------------------------------------------------------------
475/// More tests that run when the menu bar button Test2 is clicked
476fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
477    term.take_focus().unwrap();
478    term.reset_terminal();
479
480    for i in 0..50 {
481        term.append(&format!("{i}\n"));
482    }
483    assert_eq!(term.history_rows(), 100);
484
485    term.clear_history();
486    assert_eq!(term.history_use(), 0);
487
488    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
489    term.append("\nDone!\n");
490    term.set_text_attrib(Attrib::Normal);
491}
492
493//--------------------------------------------------------------------------------------
494/// Another set of tests that run when Test3 is clicked
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496    term.take_focus().unwrap();
497    term.reset_terminal();
498    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500    assert_eq!(term.history_use(), 0);
501    term.clear();
502    assert_eq!(term.cursor_row(), 0);
503    assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505    term.append("Test\ntext\na\nb\nc\nd");
506    assert_eq!(term.cursor_row(), 5);
507    let hist = term.history_use();
508    term.clear_screen_home(false);
509    assert_eq!(term.cursor_row(), 0);
510    assert_eq!(term.history_use(), hist); // History not changed
511
512    term.append("Test\ntext\na\nb\nc\nd\ne");
513    assert_eq!(term.cursor_row(), 6);
514    term.clear_screen_home(true);
515    assert_eq!(term.cursor_row(), 0);
516
517    term.append("Test\ntext\na\nb\nc\n");
518    assert_eq!(term.cursor_row(), 5);
519    term.clear_to_color(Color::DarkBlue);
520    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521    assert_eq!(term.text_bg_color(), Color::TransparentBg);
522    assert_eq!(term.cursor_row(), 0);
523
524    // Test cursor_home()
525    term.append("Test\n\n\n\n\n\n\n\n\n\n");
526    assert_eq!(term.cursor_row(), 10);
527    term.cursor_home();
528    assert_eq!(term.cursor_row(), 0);
529
530    // Test the widget color
531    assert_eq!(term.color(), Color::Black); // Default
532    term.set_color(Color::DarkGreen);
533    assert_eq!(term.color(), Color::DarkGreen);
534    term.set_color(Color::Black);
535    assert_eq!(term.color(), Color::Black);
536    term.append(
537        "This should be one line of white text on black, embedded into the top of a blue field.\n",
538    );
539
540    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541    term.set_output_translate(OutFlags::OFF);
542    assert_eq!(term.output_translate(), OutFlags::OFF);
543    term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547    term.append("\nDone!\n");
548    term.set_text_attrib(Attrib::Normal);
549}
550
551//--------------------------------------------------------------------------------------
552/// Another set of tests for the ring-buffer access methods
553/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555    let sel_len = term.selection_text_len();
556    let sel = term.selection_text();
557
558    term.take_focus().unwrap();
559    term.reset_terminal();
560    // Test the Utf8Char primitive
561    let uc = Utf8Char::new(b'Q');
562    let uc1 = uc.text_utf8();
563    assert_eq!(&uc1, b"Q");
564    assert_eq!(&uc.attrib(), &Attrib::Normal);
565    assert_eq!(
566        &uc.charflags(),
567        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568    );
569    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570    assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572    let ring_rows = term.ring_rows();
573
574    term.take_focus().unwrap();
575    term.clear_history();
576    assert_eq!(term.history_use(), 0);
577
578    // Subtract row numbers, modulo `rows`
579    fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580        match a - b {
581            n if n < 0 => n + rows,
582            n => n,
583        }
584    }
585    // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587    assert!(term.disp_srow() >= 0);
588    assert!(term.disp_erow() >= 0);
589    assert!(term.hist_srow() >= 0);
590    assert!(term.hist_erow() >= 0);
591    assert!(term.offset() >= 0);
592    assert!(term.disp_srow() <= ring_rows);
593    assert!(term.disp_erow() <= ring_rows);
594    assert!(term.hist_srow() <= ring_rows);
595    assert!(term.hist_erow() <= ring_rows);
596    assert!(term.offset() <= ring_rows);
597
598    assert_eq!(term.ring_srow(), 0);
599    assert_eq!(term.ring_erow(), ring_rows - 1);
600    assert_eq!(
601        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602        term.display_rows()
603    );
604    assert_eq!(
605        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606        term.history_rows()
607    );
608
609    assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610    assert_eq!(term.ring_srow(), 0);
611
612    /// Local function to read back all rows from the display into a long string.
613    /// Does not include scrollback history.
614    /// Trims trailing blanks on each line
615    fn read_disp(term: &Terminal) -> String {
616        let rows = term.display_rows();
617        let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618        for row in 0..rows {
619            let r = term.u8c_disp_row(row).trim();
620            // Iterate through a row, accumulating [u8]
621            for c in r.iter() {
622                // Note: Sometimes utf-8 length is > 1
623                text.extend_from_slice(c.text_utf8());
624            }
625            text.extend_from_slice(b"\n");
626        }
627        // Return the result as a string
628        std::str::from_utf8(&text).unwrap().to_string()
629    }
630
631    term.clear();
632    term.append("Top line  ↑ (up-arrow)");
633    term.set_text_attrib(Attrib::Underline);
634    term.append("  ");
635    term.set_text_attrib(Attrib::Normal);
636    term.append("  \n");
637    let mut text_out = read_disp(term);
638    // Trim trailing empty lines
639    text_out = text_out.trim_end_matches(&"\n").to_string();
640    // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642    assert_eq!(text_out, "Top line  ↑ (up-arrow)  ");
643    let r = term.u8c_disp_row(0);
644    assert_eq!(r.col(0).text_utf8(), b"T");
645    assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646    assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647    let r = term.u8c_disp_row(1);
648    assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649    assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651    // Clear the screen again, then append test text, then read it back and compare
652    let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656            Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659    term.clear_history();
660    term.clear();
661    let bg_save = term.text_bg_color();
662    let fg_save = term.text_fg_color();
663    term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664    term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665    term.append(test_text);
666    term.set_text_bg_color(bg_save);
667    term.set_text_fg_color(fg_save);
668
669    let mut text_out = read_disp(term);
670    // Trim trailing empty lines
671    text_out = text_out.trim_end_matches(&"\n").to_string();
672    assert_eq!(test_text, text_out);
673
674    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676    assert_eq!(term.ring_srow(), 0);
677    assert_eq!(term.ring_erow(), ring_rows - 1);
678    assert_eq!(
679        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680        term.display_rows()
681    );
682    assert_eq!(
683        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684        term.history_rows()
685    );
686
687    term.append(&format!(
688        "\n\nScreen has {} rows of {} columns.\n",
689        term.display_rows(),
690        term.display_columns()
691    ));
692
693    term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
695
696//--------------------------------------------------------------------------------------
697/// Yet another set of tests for misc cursor functions and other stuff
698/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700    term.take_focus().unwrap();
701
702    // Test the attr_fg_color and attr_bg_color methods.
703    // Put a single character 'A' into the buffer and check it
704    term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705    term.set_text_bg_color(Color::TransparentBg);
706    term.set_text_fg_color(Color::XtermWhite);
707    term.append("A");
708    let r = &term.u8c_disp_row(0);
709    let uc = r.col(0);
710    assert_eq!(uc.text_utf8(), b"A");
711    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712    assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713    assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714    assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715    assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717    // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718    term.clear();
719    term.set_text_fg_color_xterm(XtermColor::White);
720    term.set_text_bg_color_xterm(XtermColor::Black);
721    assert_eq!(term.text_attrib(), Attrib::Normal);
722
723    assert!(term.ansi());
724    term.append("B\x1b[32mC\x1b[1mD\n");
725
726    let r = &term.u8c_disp_row(0);
727    let uc = r.col(0);
728    assert_eq!(uc.text_utf8(), b"B");
729    assert!(uc.is_char(b'B'));
730    assert!(!uc.is_char(b'A'));
731    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735    assert_eq!(
736        &uc.charflags(),
737        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738    );
739
740    let uc = r.col(1);
741    assert_eq!(uc.text_utf8(), b"C");
742    assert!(uc.is_char(b'C'));
743    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747    assert_eq!(
748        &uc.charflags(),
749        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750    );
751
752    let uc = r.col(2);
753    assert_eq!(uc.text_utf8(), b"D");
754    assert!(uc.is_char(b'D'));
755    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757    assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759    assert_eq!(
760        &uc.attr_fgcolor(Some(term)),
761        &Color::from_rgb(0x20, 0xf0, 0x20)
762    );
763    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764    assert_eq!(
765        &uc.charflags(),
766        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767    );
768
769    // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770    term.clear();
771    term.set_text_fg_color_xterm(XtermColor::White);
772    term.set_text_bg_color_xterm(XtermColor::Black);
773    term.set_text_attrib(Attrib::Normal);
774    assert_eq!(term.text_attrib(), Attrib::Normal);
775
776    assert!(term.ansi());
777    term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779    let r = &term.u8c_disp_row(0);
780    let uc = r.col(0);
781    assert_eq!(uc.text_utf8(), b"B");
782    assert!(uc.is_char(b'B'));
783    assert!(!uc.is_char(b'A'));
784    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788    assert_eq!(
789        &uc.charflags(),
790        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791    );
792
793    let uc = r.col(1);
794    assert_eq!(uc.text_utf8(), b"C");
795    assert!(uc.is_char(b'C'));
796    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800    assert_eq!(
801        &uc.charflags(),
802        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803    );
804
805    let uc = r.col(2);
806    assert_eq!(uc.text_utf8(), b"D");
807    assert!(uc.is_char(b'D'));
808    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812    assert_eq!(
813        &uc.charflags(),
814        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815    );
816
817    let uc = r.col(3);
818    assert_eq!(uc.text_utf8(), b"E");
819    assert!(uc.is_char(b'E'));
820    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822    assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823    assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824    assert_eq!(
825        &uc.charflags(),
826        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827    );
828
829    // Test some miscellaneous Utf8 constants
830    assert_eq!(uc.length(), 1);
831    assert_eq!(uc.max_utf8(), 4);
832    assert_eq!(uc.pwidth(), 9.0);
833    assert_eq!(uc.pwidth_int(), 9);
834
835    term.set_text_fg_color_xterm(XtermColor::White);
836    term.set_text_bg_color_xterm(XtermColor::Black);
837    term.clear();
838    term.set_text_attrib(Attrib::Normal);
839
840    // Mouse selection functions
841    term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842    term.clear_mouse_selection();
843    assert_eq!(term.get_selection(), None);
844
845    // Play with cursor position
846    term.append("0123456789\n"); // Set up test pattern
847    term.append("ABCDEFGHIJ\n");
848    term.append("abcdefghij\n");
849
850    term.set_cursor_row(1);
851    assert_eq!(term.cursor_row(), 1);
852    term.set_cursor_col(1);
853    assert_eq!(term.cursor_col(), 1);
854    assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856    term.append("----"); // Overwrites text at cursor and moves cursor forward
857    assert_eq!(term.cursor_row(), 1);
858    assert_eq!(term.cursor_col(), 5);
859    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860    term.set_cursor_col(1);
861    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863    term.cursor_up(1, false);
864    assert_eq!(term.cursor_row(), 0);
865    assert_eq!(term.cursor_col(), 1);
866    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868    // Hit top of screen, so nothing happens
869    term.cursor_up(1, false);
870    assert_eq!(term.cursor_row(), 0);
871    assert_eq!(term.cursor_col(), 1);
872    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874    // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875    term.cursor_up(1, true);
876    assert_eq!(term.cursor_row(), 0);
877    assert_eq!(term.cursor_col(), 1);
878    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880    // Go back down to the overwritten text
881    term.cursor_down(2, false);
882    assert_eq!(term.cursor_row(), 2);
883    assert_eq!(term.cursor_col(), 1);
884    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886    // Go right past the overwritten text
887    term.cursor_right(4, false);
888    assert_eq!(term.cursor_row(), 2);
889    assert_eq!(term.cursor_col(), 5);
890    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892    // Go left to the end of the overwritten text
893    term.cursor_left(1);
894    assert_eq!(term.cursor_row(), 2);
895    assert_eq!(term.cursor_col(), 4);
896    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898    // Scroll back down, removing the blank line at the top.
899    // Cursor stays in place, the text moves under it.
900    term.scroll(1);
901    assert_eq!(term.cursor_row(), 2);
902    assert_eq!(term.cursor_col(), 4);
903    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905    // Clear from here to end-of-line
906    term.clear_eol();
907    assert_eq!(term.cursor_row(), 2);
908    assert_eq!(term.cursor_col(), 4);
909    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911    // Now clear from here to start-of-line. Cursor does not move.
912    term.clear_sol();
913    assert_eq!(term.cursor_row(), 2);
914    assert_eq!(term.cursor_col(), 4);
915    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916    term.cursor_left(1);
917    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918    term.set_cursor_col(0);
919    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921    // Clear some lines
922    term.clear_line(1);
923    assert_eq!(term.cursor_row(), 2);
924    assert_eq!(term.cursor_col(), 0);
925    term.set_cursor_row(1);
926    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927    term.set_cursor_row(3);
928    term.clear_cur_line();
929    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930    assert_eq!(term.cursor_row(), 3);
931    assert_eq!(term.cursor_col(), 0);
932
933    term.append("Two lines above are intentionally left blank.\n");
934    assert_eq!(term.cursor_row(), 4);
935    assert_eq!(term.cursor_col(), 0);
936
937    // Set up the test pattern again, then play with insert/delete
938    term.append("0123456789\n");
939    term.append("ABCDEFGHIJ\n");
940    term.append("abcdefghij\n");
941    assert_eq!(term.cursor_row(), 7);
942
943    term.set_cursor_row(4);
944    term.set_cursor_col(4);
945    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947    term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949    term.cursor_right(5, false);
950    assert_eq!(term.cursor_col(), 9);
951    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953    // Insert two blank rows above cursor. Cursor stays put.
954    term.insert_rows(2);
955    assert_eq!(term.cursor_row(), 4);
956    assert_eq!(term.cursor_col(), 9);
957    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958    term.cursor_down(2, false); // Go down to find our text again
959    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961    // Go back to the beginning of the inserted 'x' characters and delete them.
962    term.cursor_left(5);
963    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964    term.delete_cur_chars(5);
965    assert_eq!(term.cursor_row(), 6);
966    assert_eq!(term.cursor_col(), 4);
967    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969    term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970    term.cursor_down(1, false);
971    term.cursor_left(2);
972    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974    term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975    assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976    term.cursor_up(1, false);
977    term.delete_rows(2); // Delete remains of test pattern
978
979    term.set_text_attrib(Attrib::Bold);
980    term.insert_char_eol('-', 3, 15, 20);
981    term.set_cursor_row(3);
982    term.set_cursor_col(15);
983    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984    assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986    term.set_text_attrib(Attrib::Italic);
987    term.append(" and all lines below");
988    term.set_text_attrib(Attrib::Normal);
989    term.cursor_down(1, false);
990
991    let mut hsb = term.hscrollbar();
992    let mut sb = term.scrollbar();
993    hsb.set_value(100.0);
994    assert_eq!(hsb.value(), 100.0);
995    sb.set_value(50.0);
996    assert_eq!(sb.value(), 50.0);
997    hsb.set_value(0.0);
998    assert_eq!(hsb.value(), 0.0);
999    sb.set_value(0.0);
1000    assert_eq!(sb.value(), 0.0);
1001}
Source

pub fn set_cursor_row(&mut self, val: i32)

Set the cursor’s current row position on the screen. This is a low-level “protected” function of the fltk library

Examples found in repository?
examples/terminal.rs (line 850)
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700    term.take_focus().unwrap();
701
702    // Test the attr_fg_color and attr_bg_color methods.
703    // Put a single character 'A' into the buffer and check it
704    term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705    term.set_text_bg_color(Color::TransparentBg);
706    term.set_text_fg_color(Color::XtermWhite);
707    term.append("A");
708    let r = &term.u8c_disp_row(0);
709    let uc = r.col(0);
710    assert_eq!(uc.text_utf8(), b"A");
711    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712    assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713    assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714    assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715    assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717    // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718    term.clear();
719    term.set_text_fg_color_xterm(XtermColor::White);
720    term.set_text_bg_color_xterm(XtermColor::Black);
721    assert_eq!(term.text_attrib(), Attrib::Normal);
722
723    assert!(term.ansi());
724    term.append("B\x1b[32mC\x1b[1mD\n");
725
726    let r = &term.u8c_disp_row(0);
727    let uc = r.col(0);
728    assert_eq!(uc.text_utf8(), b"B");
729    assert!(uc.is_char(b'B'));
730    assert!(!uc.is_char(b'A'));
731    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735    assert_eq!(
736        &uc.charflags(),
737        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738    );
739
740    let uc = r.col(1);
741    assert_eq!(uc.text_utf8(), b"C");
742    assert!(uc.is_char(b'C'));
743    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747    assert_eq!(
748        &uc.charflags(),
749        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750    );
751
752    let uc = r.col(2);
753    assert_eq!(uc.text_utf8(), b"D");
754    assert!(uc.is_char(b'D'));
755    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757    assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759    assert_eq!(
760        &uc.attr_fgcolor(Some(term)),
761        &Color::from_rgb(0x20, 0xf0, 0x20)
762    );
763    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764    assert_eq!(
765        &uc.charflags(),
766        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767    );
768
769    // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770    term.clear();
771    term.set_text_fg_color_xterm(XtermColor::White);
772    term.set_text_bg_color_xterm(XtermColor::Black);
773    term.set_text_attrib(Attrib::Normal);
774    assert_eq!(term.text_attrib(), Attrib::Normal);
775
776    assert!(term.ansi());
777    term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779    let r = &term.u8c_disp_row(0);
780    let uc = r.col(0);
781    assert_eq!(uc.text_utf8(), b"B");
782    assert!(uc.is_char(b'B'));
783    assert!(!uc.is_char(b'A'));
784    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788    assert_eq!(
789        &uc.charflags(),
790        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791    );
792
793    let uc = r.col(1);
794    assert_eq!(uc.text_utf8(), b"C");
795    assert!(uc.is_char(b'C'));
796    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800    assert_eq!(
801        &uc.charflags(),
802        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803    );
804
805    let uc = r.col(2);
806    assert_eq!(uc.text_utf8(), b"D");
807    assert!(uc.is_char(b'D'));
808    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812    assert_eq!(
813        &uc.charflags(),
814        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815    );
816
817    let uc = r.col(3);
818    assert_eq!(uc.text_utf8(), b"E");
819    assert!(uc.is_char(b'E'));
820    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822    assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823    assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824    assert_eq!(
825        &uc.charflags(),
826        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827    );
828
829    // Test some miscellaneous Utf8 constants
830    assert_eq!(uc.length(), 1);
831    assert_eq!(uc.max_utf8(), 4);
832    assert_eq!(uc.pwidth(), 9.0);
833    assert_eq!(uc.pwidth_int(), 9);
834
835    term.set_text_fg_color_xterm(XtermColor::White);
836    term.set_text_bg_color_xterm(XtermColor::Black);
837    term.clear();
838    term.set_text_attrib(Attrib::Normal);
839
840    // Mouse selection functions
841    term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842    term.clear_mouse_selection();
843    assert_eq!(term.get_selection(), None);
844
845    // Play with cursor position
846    term.append("0123456789\n"); // Set up test pattern
847    term.append("ABCDEFGHIJ\n");
848    term.append("abcdefghij\n");
849
850    term.set_cursor_row(1);
851    assert_eq!(term.cursor_row(), 1);
852    term.set_cursor_col(1);
853    assert_eq!(term.cursor_col(), 1);
854    assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856    term.append("----"); // Overwrites text at cursor and moves cursor forward
857    assert_eq!(term.cursor_row(), 1);
858    assert_eq!(term.cursor_col(), 5);
859    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860    term.set_cursor_col(1);
861    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863    term.cursor_up(1, false);
864    assert_eq!(term.cursor_row(), 0);
865    assert_eq!(term.cursor_col(), 1);
866    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868    // Hit top of screen, so nothing happens
869    term.cursor_up(1, false);
870    assert_eq!(term.cursor_row(), 0);
871    assert_eq!(term.cursor_col(), 1);
872    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874    // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875    term.cursor_up(1, true);
876    assert_eq!(term.cursor_row(), 0);
877    assert_eq!(term.cursor_col(), 1);
878    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880    // Go back down to the overwritten text
881    term.cursor_down(2, false);
882    assert_eq!(term.cursor_row(), 2);
883    assert_eq!(term.cursor_col(), 1);
884    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886    // Go right past the overwritten text
887    term.cursor_right(4, false);
888    assert_eq!(term.cursor_row(), 2);
889    assert_eq!(term.cursor_col(), 5);
890    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892    // Go left to the end of the overwritten text
893    term.cursor_left(1);
894    assert_eq!(term.cursor_row(), 2);
895    assert_eq!(term.cursor_col(), 4);
896    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898    // Scroll back down, removing the blank line at the top.
899    // Cursor stays in place, the text moves under it.
900    term.scroll(1);
901    assert_eq!(term.cursor_row(), 2);
902    assert_eq!(term.cursor_col(), 4);
903    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905    // Clear from here to end-of-line
906    term.clear_eol();
907    assert_eq!(term.cursor_row(), 2);
908    assert_eq!(term.cursor_col(), 4);
909    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911    // Now clear from here to start-of-line. Cursor does not move.
912    term.clear_sol();
913    assert_eq!(term.cursor_row(), 2);
914    assert_eq!(term.cursor_col(), 4);
915    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916    term.cursor_left(1);
917    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918    term.set_cursor_col(0);
919    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921    // Clear some lines
922    term.clear_line(1);
923    assert_eq!(term.cursor_row(), 2);
924    assert_eq!(term.cursor_col(), 0);
925    term.set_cursor_row(1);
926    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927    term.set_cursor_row(3);
928    term.clear_cur_line();
929    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930    assert_eq!(term.cursor_row(), 3);
931    assert_eq!(term.cursor_col(), 0);
932
933    term.append("Two lines above are intentionally left blank.\n");
934    assert_eq!(term.cursor_row(), 4);
935    assert_eq!(term.cursor_col(), 0);
936
937    // Set up the test pattern again, then play with insert/delete
938    term.append("0123456789\n");
939    term.append("ABCDEFGHIJ\n");
940    term.append("abcdefghij\n");
941    assert_eq!(term.cursor_row(), 7);
942
943    term.set_cursor_row(4);
944    term.set_cursor_col(4);
945    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947    term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949    term.cursor_right(5, false);
950    assert_eq!(term.cursor_col(), 9);
951    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953    // Insert two blank rows above cursor. Cursor stays put.
954    term.insert_rows(2);
955    assert_eq!(term.cursor_row(), 4);
956    assert_eq!(term.cursor_col(), 9);
957    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958    term.cursor_down(2, false); // Go down to find our text again
959    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961    // Go back to the beginning of the inserted 'x' characters and delete them.
962    term.cursor_left(5);
963    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964    term.delete_cur_chars(5);
965    assert_eq!(term.cursor_row(), 6);
966    assert_eq!(term.cursor_col(), 4);
967    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969    term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970    term.cursor_down(1, false);
971    term.cursor_left(2);
972    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974    term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975    assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976    term.cursor_up(1, false);
977    term.delete_rows(2); // Delete remains of test pattern
978
979    term.set_text_attrib(Attrib::Bold);
980    term.insert_char_eol('-', 3, 15, 20);
981    term.set_cursor_row(3);
982    term.set_cursor_col(15);
983    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984    assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986    term.set_text_attrib(Attrib::Italic);
987    term.append(" and all lines below");
988    term.set_text_attrib(Attrib::Normal);
989    term.cursor_down(1, false);
990
991    let mut hsb = term.hscrollbar();
992    let mut sb = term.scrollbar();
993    hsb.set_value(100.0);
994    assert_eq!(hsb.value(), 100.0);
995    sb.set_value(50.0);
996    assert_eq!(sb.value(), 50.0);
997    hsb.set_value(0.0);
998    assert_eq!(hsb.value(), 0.0);
999    sb.set_value(0.0);
1000    assert_eq!(sb.value(), 0.0);
1001}
Source

pub fn cursor_up(&mut self, count: i32, do_scroll: bool)

Moves cursor up count lines. If cursor hits screen top, it either stops (does not wrap) if do_scroll is false, or scrolls down if do_scroll is true. This is a low-level “protected” function of the fltk library

Examples found in repository?
examples/terminal.rs (line 112)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
425//--------------------------------------------------------------------------------------
426/// More tests that run when the menu bar Test1 is clicked
427fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
428    term.take_focus().unwrap();
429    term.reset_terminal();
430    term.append("0123456789 0\n");
431    term.append("0123456789 1\n");
432    term.append("0123456789 2\n");
433    term.append("0123456789 3\n");
434    term.append("0123456789 4\n");
435    term.append("0123456789 5\n");
436    term.append("0123456789 6\n");
437    term.append("0123456789 7\n");
438    term.append("0123456789 8\n");
439    term.append("0123456789 9\n");
440    term.append("------------\n");
441
442    term.set_text_fg_color(Color::Green);
443    term.plot_char('A', 0, 0);
444    term.plot_char('B', 1, 1);
445    term.plot_char('C', 2, 2);
446    term.plot_char('D', 3, 3);
447    term.plot_char('E', 4, 4);
448    term.plot_char('F', 5, 5);
449    term.set_text_fg_color(Color::XtermWhite);
450
451    assert_eq!(term.cursor_row(), 11);
452    assert_eq!(term.cursor_col(), 0);
453
454    term.set_text_bg_color(Color::DarkBlue);
455    term.plot_char_utf8('b', 8, 1);
456    term.plot_char_utf8('↑', 9, 1);
457    term.plot_char_utf8('c', 8, 2);
458    term.plot_char_utf8('↑', 9, 2);
459    term.plot_char_utf8('d', 8, 3);
460    term.plot_char_utf8('↑', 9, 3);
461    term.plot_char_utf8('e', 8, 4);
462    term.plot_char_utf8('↑', 9, 4);
463    term.plot_char_utf8('f', 8, 5);
464    term.plot_char_utf8('↑', 9, 5);
465    term.plot_char_utf8('g', 8, 6);
466    term.plot_char_utf8('↑', 9, 6);
467    term.set_text_bg_color(Color::TransparentBg);
468
469    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
470    term.append("Done!\n");
471    term.set_text_attrib(Attrib::Normal);
472}
473
474//--------------------------------------------------------------------------------------
475/// More tests that run when the menu bar button Test2 is clicked
476fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
477    term.take_focus().unwrap();
478    term.reset_terminal();
479
480    for i in 0..50 {
481        term.append(&format!("{i}\n"));
482    }
483    assert_eq!(term.history_rows(), 100);
484
485    term.clear_history();
486    assert_eq!(term.history_use(), 0);
487
488    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
489    term.append("\nDone!\n");
490    term.set_text_attrib(Attrib::Normal);
491}
492
493//--------------------------------------------------------------------------------------
494/// Another set of tests that run when Test3 is clicked
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496    term.take_focus().unwrap();
497    term.reset_terminal();
498    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500    assert_eq!(term.history_use(), 0);
501    term.clear();
502    assert_eq!(term.cursor_row(), 0);
503    assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505    term.append("Test\ntext\na\nb\nc\nd");
506    assert_eq!(term.cursor_row(), 5);
507    let hist = term.history_use();
508    term.clear_screen_home(false);
509    assert_eq!(term.cursor_row(), 0);
510    assert_eq!(term.history_use(), hist); // History not changed
511
512    term.append("Test\ntext\na\nb\nc\nd\ne");
513    assert_eq!(term.cursor_row(), 6);
514    term.clear_screen_home(true);
515    assert_eq!(term.cursor_row(), 0);
516
517    term.append("Test\ntext\na\nb\nc\n");
518    assert_eq!(term.cursor_row(), 5);
519    term.clear_to_color(Color::DarkBlue);
520    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521    assert_eq!(term.text_bg_color(), Color::TransparentBg);
522    assert_eq!(term.cursor_row(), 0);
523
524    // Test cursor_home()
525    term.append("Test\n\n\n\n\n\n\n\n\n\n");
526    assert_eq!(term.cursor_row(), 10);
527    term.cursor_home();
528    assert_eq!(term.cursor_row(), 0);
529
530    // Test the widget color
531    assert_eq!(term.color(), Color::Black); // Default
532    term.set_color(Color::DarkGreen);
533    assert_eq!(term.color(), Color::DarkGreen);
534    term.set_color(Color::Black);
535    assert_eq!(term.color(), Color::Black);
536    term.append(
537        "This should be one line of white text on black, embedded into the top of a blue field.\n",
538    );
539
540    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541    term.set_output_translate(OutFlags::OFF);
542    assert_eq!(term.output_translate(), OutFlags::OFF);
543    term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547    term.append("\nDone!\n");
548    term.set_text_attrib(Attrib::Normal);
549}
550
551//--------------------------------------------------------------------------------------
552/// Another set of tests for the ring-buffer access methods
553/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555    let sel_len = term.selection_text_len();
556    let sel = term.selection_text();
557
558    term.take_focus().unwrap();
559    term.reset_terminal();
560    // Test the Utf8Char primitive
561    let uc = Utf8Char::new(b'Q');
562    let uc1 = uc.text_utf8();
563    assert_eq!(&uc1, b"Q");
564    assert_eq!(&uc.attrib(), &Attrib::Normal);
565    assert_eq!(
566        &uc.charflags(),
567        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568    );
569    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570    assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572    let ring_rows = term.ring_rows();
573
574    term.take_focus().unwrap();
575    term.clear_history();
576    assert_eq!(term.history_use(), 0);
577
578    // Subtract row numbers, modulo `rows`
579    fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580        match a - b {
581            n if n < 0 => n + rows,
582            n => n,
583        }
584    }
585    // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587    assert!(term.disp_srow() >= 0);
588    assert!(term.disp_erow() >= 0);
589    assert!(term.hist_srow() >= 0);
590    assert!(term.hist_erow() >= 0);
591    assert!(term.offset() >= 0);
592    assert!(term.disp_srow() <= ring_rows);
593    assert!(term.disp_erow() <= ring_rows);
594    assert!(term.hist_srow() <= ring_rows);
595    assert!(term.hist_erow() <= ring_rows);
596    assert!(term.offset() <= ring_rows);
597
598    assert_eq!(term.ring_srow(), 0);
599    assert_eq!(term.ring_erow(), ring_rows - 1);
600    assert_eq!(
601        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602        term.display_rows()
603    );
604    assert_eq!(
605        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606        term.history_rows()
607    );
608
609    assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610    assert_eq!(term.ring_srow(), 0);
611
612    /// Local function to read back all rows from the display into a long string.
613    /// Does not include scrollback history.
614    /// Trims trailing blanks on each line
615    fn read_disp(term: &Terminal) -> String {
616        let rows = term.display_rows();
617        let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618        for row in 0..rows {
619            let r = term.u8c_disp_row(row).trim();
620            // Iterate through a row, accumulating [u8]
621            for c in r.iter() {
622                // Note: Sometimes utf-8 length is > 1
623                text.extend_from_slice(c.text_utf8());
624            }
625            text.extend_from_slice(b"\n");
626        }
627        // Return the result as a string
628        std::str::from_utf8(&text).unwrap().to_string()
629    }
630
631    term.clear();
632    term.append("Top line  ↑ (up-arrow)");
633    term.set_text_attrib(Attrib::Underline);
634    term.append("  ");
635    term.set_text_attrib(Attrib::Normal);
636    term.append("  \n");
637    let mut text_out = read_disp(term);
638    // Trim trailing empty lines
639    text_out = text_out.trim_end_matches(&"\n").to_string();
640    // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642    assert_eq!(text_out, "Top line  ↑ (up-arrow)  ");
643    let r = term.u8c_disp_row(0);
644    assert_eq!(r.col(0).text_utf8(), b"T");
645    assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646    assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647    let r = term.u8c_disp_row(1);
648    assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649    assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651    // Clear the screen again, then append test text, then read it back and compare
652    let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656            Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659    term.clear_history();
660    term.clear();
661    let bg_save = term.text_bg_color();
662    let fg_save = term.text_fg_color();
663    term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664    term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665    term.append(test_text);
666    term.set_text_bg_color(bg_save);
667    term.set_text_fg_color(fg_save);
668
669    let mut text_out = read_disp(term);
670    // Trim trailing empty lines
671    text_out = text_out.trim_end_matches(&"\n").to_string();
672    assert_eq!(test_text, text_out);
673
674    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676    assert_eq!(term.ring_srow(), 0);
677    assert_eq!(term.ring_erow(), ring_rows - 1);
678    assert_eq!(
679        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680        term.display_rows()
681    );
682    assert_eq!(
683        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684        term.history_rows()
685    );
686
687    term.append(&format!(
688        "\n\nScreen has {} rows of {} columns.\n",
689        term.display_rows(),
690        term.display_columns()
691    ));
692
693    term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
695
696//--------------------------------------------------------------------------------------
697/// Yet another set of tests for misc cursor functions and other stuff
698/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700    term.take_focus().unwrap();
701
702    // Test the attr_fg_color and attr_bg_color methods.
703    // Put a single character 'A' into the buffer and check it
704    term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705    term.set_text_bg_color(Color::TransparentBg);
706    term.set_text_fg_color(Color::XtermWhite);
707    term.append("A");
708    let r = &term.u8c_disp_row(0);
709    let uc = r.col(0);
710    assert_eq!(uc.text_utf8(), b"A");
711    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712    assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713    assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714    assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715    assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717    // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718    term.clear();
719    term.set_text_fg_color_xterm(XtermColor::White);
720    term.set_text_bg_color_xterm(XtermColor::Black);
721    assert_eq!(term.text_attrib(), Attrib::Normal);
722
723    assert!(term.ansi());
724    term.append("B\x1b[32mC\x1b[1mD\n");
725
726    let r = &term.u8c_disp_row(0);
727    let uc = r.col(0);
728    assert_eq!(uc.text_utf8(), b"B");
729    assert!(uc.is_char(b'B'));
730    assert!(!uc.is_char(b'A'));
731    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735    assert_eq!(
736        &uc.charflags(),
737        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738    );
739
740    let uc = r.col(1);
741    assert_eq!(uc.text_utf8(), b"C");
742    assert!(uc.is_char(b'C'));
743    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747    assert_eq!(
748        &uc.charflags(),
749        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750    );
751
752    let uc = r.col(2);
753    assert_eq!(uc.text_utf8(), b"D");
754    assert!(uc.is_char(b'D'));
755    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757    assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759    assert_eq!(
760        &uc.attr_fgcolor(Some(term)),
761        &Color::from_rgb(0x20, 0xf0, 0x20)
762    );
763    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764    assert_eq!(
765        &uc.charflags(),
766        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767    );
768
769    // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770    term.clear();
771    term.set_text_fg_color_xterm(XtermColor::White);
772    term.set_text_bg_color_xterm(XtermColor::Black);
773    term.set_text_attrib(Attrib::Normal);
774    assert_eq!(term.text_attrib(), Attrib::Normal);
775
776    assert!(term.ansi());
777    term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779    let r = &term.u8c_disp_row(0);
780    let uc = r.col(0);
781    assert_eq!(uc.text_utf8(), b"B");
782    assert!(uc.is_char(b'B'));
783    assert!(!uc.is_char(b'A'));
784    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788    assert_eq!(
789        &uc.charflags(),
790        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791    );
792
793    let uc = r.col(1);
794    assert_eq!(uc.text_utf8(), b"C");
795    assert!(uc.is_char(b'C'));
796    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800    assert_eq!(
801        &uc.charflags(),
802        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803    );
804
805    let uc = r.col(2);
806    assert_eq!(uc.text_utf8(), b"D");
807    assert!(uc.is_char(b'D'));
808    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812    assert_eq!(
813        &uc.charflags(),
814        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815    );
816
817    let uc = r.col(3);
818    assert_eq!(uc.text_utf8(), b"E");
819    assert!(uc.is_char(b'E'));
820    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822    assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823    assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824    assert_eq!(
825        &uc.charflags(),
826        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827    );
828
829    // Test some miscellaneous Utf8 constants
830    assert_eq!(uc.length(), 1);
831    assert_eq!(uc.max_utf8(), 4);
832    assert_eq!(uc.pwidth(), 9.0);
833    assert_eq!(uc.pwidth_int(), 9);
834
835    term.set_text_fg_color_xterm(XtermColor::White);
836    term.set_text_bg_color_xterm(XtermColor::Black);
837    term.clear();
838    term.set_text_attrib(Attrib::Normal);
839
840    // Mouse selection functions
841    term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842    term.clear_mouse_selection();
843    assert_eq!(term.get_selection(), None);
844
845    // Play with cursor position
846    term.append("0123456789\n"); // Set up test pattern
847    term.append("ABCDEFGHIJ\n");
848    term.append("abcdefghij\n");
849
850    term.set_cursor_row(1);
851    assert_eq!(term.cursor_row(), 1);
852    term.set_cursor_col(1);
853    assert_eq!(term.cursor_col(), 1);
854    assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856    term.append("----"); // Overwrites text at cursor and moves cursor forward
857    assert_eq!(term.cursor_row(), 1);
858    assert_eq!(term.cursor_col(), 5);
859    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860    term.set_cursor_col(1);
861    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863    term.cursor_up(1, false);
864    assert_eq!(term.cursor_row(), 0);
865    assert_eq!(term.cursor_col(), 1);
866    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868    // Hit top of screen, so nothing happens
869    term.cursor_up(1, false);
870    assert_eq!(term.cursor_row(), 0);
871    assert_eq!(term.cursor_col(), 1);
872    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874    // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875    term.cursor_up(1, true);
876    assert_eq!(term.cursor_row(), 0);
877    assert_eq!(term.cursor_col(), 1);
878    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880    // Go back down to the overwritten text
881    term.cursor_down(2, false);
882    assert_eq!(term.cursor_row(), 2);
883    assert_eq!(term.cursor_col(), 1);
884    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886    // Go right past the overwritten text
887    term.cursor_right(4, false);
888    assert_eq!(term.cursor_row(), 2);
889    assert_eq!(term.cursor_col(), 5);
890    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892    // Go left to the end of the overwritten text
893    term.cursor_left(1);
894    assert_eq!(term.cursor_row(), 2);
895    assert_eq!(term.cursor_col(), 4);
896    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898    // Scroll back down, removing the blank line at the top.
899    // Cursor stays in place, the text moves under it.
900    term.scroll(1);
901    assert_eq!(term.cursor_row(), 2);
902    assert_eq!(term.cursor_col(), 4);
903    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905    // Clear from here to end-of-line
906    term.clear_eol();
907    assert_eq!(term.cursor_row(), 2);
908    assert_eq!(term.cursor_col(), 4);
909    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911    // Now clear from here to start-of-line. Cursor does not move.
912    term.clear_sol();
913    assert_eq!(term.cursor_row(), 2);
914    assert_eq!(term.cursor_col(), 4);
915    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916    term.cursor_left(1);
917    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918    term.set_cursor_col(0);
919    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921    // Clear some lines
922    term.clear_line(1);
923    assert_eq!(term.cursor_row(), 2);
924    assert_eq!(term.cursor_col(), 0);
925    term.set_cursor_row(1);
926    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927    term.set_cursor_row(3);
928    term.clear_cur_line();
929    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930    assert_eq!(term.cursor_row(), 3);
931    assert_eq!(term.cursor_col(), 0);
932
933    term.append("Two lines above are intentionally left blank.\n");
934    assert_eq!(term.cursor_row(), 4);
935    assert_eq!(term.cursor_col(), 0);
936
937    // Set up the test pattern again, then play with insert/delete
938    term.append("0123456789\n");
939    term.append("ABCDEFGHIJ\n");
940    term.append("abcdefghij\n");
941    assert_eq!(term.cursor_row(), 7);
942
943    term.set_cursor_row(4);
944    term.set_cursor_col(4);
945    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947    term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949    term.cursor_right(5, false);
950    assert_eq!(term.cursor_col(), 9);
951    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953    // Insert two blank rows above cursor. Cursor stays put.
954    term.insert_rows(2);
955    assert_eq!(term.cursor_row(), 4);
956    assert_eq!(term.cursor_col(), 9);
957    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958    term.cursor_down(2, false); // Go down to find our text again
959    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961    // Go back to the beginning of the inserted 'x' characters and delete them.
962    term.cursor_left(5);
963    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964    term.delete_cur_chars(5);
965    assert_eq!(term.cursor_row(), 6);
966    assert_eq!(term.cursor_col(), 4);
967    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969    term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970    term.cursor_down(1, false);
971    term.cursor_left(2);
972    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974    term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975    assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976    term.cursor_up(1, false);
977    term.delete_rows(2); // Delete remains of test pattern
978
979    term.set_text_attrib(Attrib::Bold);
980    term.insert_char_eol('-', 3, 15, 20);
981    term.set_cursor_row(3);
982    term.set_cursor_col(15);
983    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984    assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986    term.set_text_attrib(Attrib::Italic);
987    term.append(" and all lines below");
988    term.set_text_attrib(Attrib::Normal);
989    term.cursor_down(1, false);
990
991    let mut hsb = term.hscrollbar();
992    let mut sb = term.scrollbar();
993    hsb.set_value(100.0);
994    assert_eq!(hsb.value(), 100.0);
995    sb.set_value(50.0);
996    assert_eq!(sb.value(), 50.0);
997    hsb.set_value(0.0);
998    assert_eq!(hsb.value(), 0.0);
999    sb.set_value(0.0);
1000    assert_eq!(sb.value(), 0.0);
1001}
Source

pub fn cursor_down(&mut self, count: i32, do_scroll: bool)

Moves cursor down count lines. If cursor hits screen bottom, it either stops (does not wrap) if do_scroll is false, or wraps and scrolls up if do_scroll is true. This is a low-level “protected” function of the fltk library

Examples found in repository?
examples/terminal.rs (line 881)
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700    term.take_focus().unwrap();
701
702    // Test the attr_fg_color and attr_bg_color methods.
703    // Put a single character 'A' into the buffer and check it
704    term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705    term.set_text_bg_color(Color::TransparentBg);
706    term.set_text_fg_color(Color::XtermWhite);
707    term.append("A");
708    let r = &term.u8c_disp_row(0);
709    let uc = r.col(0);
710    assert_eq!(uc.text_utf8(), b"A");
711    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712    assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713    assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714    assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715    assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717    // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718    term.clear();
719    term.set_text_fg_color_xterm(XtermColor::White);
720    term.set_text_bg_color_xterm(XtermColor::Black);
721    assert_eq!(term.text_attrib(), Attrib::Normal);
722
723    assert!(term.ansi());
724    term.append("B\x1b[32mC\x1b[1mD\n");
725
726    let r = &term.u8c_disp_row(0);
727    let uc = r.col(0);
728    assert_eq!(uc.text_utf8(), b"B");
729    assert!(uc.is_char(b'B'));
730    assert!(!uc.is_char(b'A'));
731    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735    assert_eq!(
736        &uc.charflags(),
737        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738    );
739
740    let uc = r.col(1);
741    assert_eq!(uc.text_utf8(), b"C");
742    assert!(uc.is_char(b'C'));
743    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747    assert_eq!(
748        &uc.charflags(),
749        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750    );
751
752    let uc = r.col(2);
753    assert_eq!(uc.text_utf8(), b"D");
754    assert!(uc.is_char(b'D'));
755    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757    assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759    assert_eq!(
760        &uc.attr_fgcolor(Some(term)),
761        &Color::from_rgb(0x20, 0xf0, 0x20)
762    );
763    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764    assert_eq!(
765        &uc.charflags(),
766        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767    );
768
769    // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770    term.clear();
771    term.set_text_fg_color_xterm(XtermColor::White);
772    term.set_text_bg_color_xterm(XtermColor::Black);
773    term.set_text_attrib(Attrib::Normal);
774    assert_eq!(term.text_attrib(), Attrib::Normal);
775
776    assert!(term.ansi());
777    term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779    let r = &term.u8c_disp_row(0);
780    let uc = r.col(0);
781    assert_eq!(uc.text_utf8(), b"B");
782    assert!(uc.is_char(b'B'));
783    assert!(!uc.is_char(b'A'));
784    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788    assert_eq!(
789        &uc.charflags(),
790        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791    );
792
793    let uc = r.col(1);
794    assert_eq!(uc.text_utf8(), b"C");
795    assert!(uc.is_char(b'C'));
796    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800    assert_eq!(
801        &uc.charflags(),
802        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803    );
804
805    let uc = r.col(2);
806    assert_eq!(uc.text_utf8(), b"D");
807    assert!(uc.is_char(b'D'));
808    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812    assert_eq!(
813        &uc.charflags(),
814        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815    );
816
817    let uc = r.col(3);
818    assert_eq!(uc.text_utf8(), b"E");
819    assert!(uc.is_char(b'E'));
820    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822    assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823    assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824    assert_eq!(
825        &uc.charflags(),
826        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827    );
828
829    // Test some miscellaneous Utf8 constants
830    assert_eq!(uc.length(), 1);
831    assert_eq!(uc.max_utf8(), 4);
832    assert_eq!(uc.pwidth(), 9.0);
833    assert_eq!(uc.pwidth_int(), 9);
834
835    term.set_text_fg_color_xterm(XtermColor::White);
836    term.set_text_bg_color_xterm(XtermColor::Black);
837    term.clear();
838    term.set_text_attrib(Attrib::Normal);
839
840    // Mouse selection functions
841    term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842    term.clear_mouse_selection();
843    assert_eq!(term.get_selection(), None);
844
845    // Play with cursor position
846    term.append("0123456789\n"); // Set up test pattern
847    term.append("ABCDEFGHIJ\n");
848    term.append("abcdefghij\n");
849
850    term.set_cursor_row(1);
851    assert_eq!(term.cursor_row(), 1);
852    term.set_cursor_col(1);
853    assert_eq!(term.cursor_col(), 1);
854    assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856    term.append("----"); // Overwrites text at cursor and moves cursor forward
857    assert_eq!(term.cursor_row(), 1);
858    assert_eq!(term.cursor_col(), 5);
859    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860    term.set_cursor_col(1);
861    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863    term.cursor_up(1, false);
864    assert_eq!(term.cursor_row(), 0);
865    assert_eq!(term.cursor_col(), 1);
866    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868    // Hit top of screen, so nothing happens
869    term.cursor_up(1, false);
870    assert_eq!(term.cursor_row(), 0);
871    assert_eq!(term.cursor_col(), 1);
872    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874    // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875    term.cursor_up(1, true);
876    assert_eq!(term.cursor_row(), 0);
877    assert_eq!(term.cursor_col(), 1);
878    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880    // Go back down to the overwritten text
881    term.cursor_down(2, false);
882    assert_eq!(term.cursor_row(), 2);
883    assert_eq!(term.cursor_col(), 1);
884    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886    // Go right past the overwritten text
887    term.cursor_right(4, false);
888    assert_eq!(term.cursor_row(), 2);
889    assert_eq!(term.cursor_col(), 5);
890    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892    // Go left to the end of the overwritten text
893    term.cursor_left(1);
894    assert_eq!(term.cursor_row(), 2);
895    assert_eq!(term.cursor_col(), 4);
896    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898    // Scroll back down, removing the blank line at the top.
899    // Cursor stays in place, the text moves under it.
900    term.scroll(1);
901    assert_eq!(term.cursor_row(), 2);
902    assert_eq!(term.cursor_col(), 4);
903    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905    // Clear from here to end-of-line
906    term.clear_eol();
907    assert_eq!(term.cursor_row(), 2);
908    assert_eq!(term.cursor_col(), 4);
909    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911    // Now clear from here to start-of-line. Cursor does not move.
912    term.clear_sol();
913    assert_eq!(term.cursor_row(), 2);
914    assert_eq!(term.cursor_col(), 4);
915    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916    term.cursor_left(1);
917    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918    term.set_cursor_col(0);
919    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921    // Clear some lines
922    term.clear_line(1);
923    assert_eq!(term.cursor_row(), 2);
924    assert_eq!(term.cursor_col(), 0);
925    term.set_cursor_row(1);
926    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927    term.set_cursor_row(3);
928    term.clear_cur_line();
929    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930    assert_eq!(term.cursor_row(), 3);
931    assert_eq!(term.cursor_col(), 0);
932
933    term.append("Two lines above are intentionally left blank.\n");
934    assert_eq!(term.cursor_row(), 4);
935    assert_eq!(term.cursor_col(), 0);
936
937    // Set up the test pattern again, then play with insert/delete
938    term.append("0123456789\n");
939    term.append("ABCDEFGHIJ\n");
940    term.append("abcdefghij\n");
941    assert_eq!(term.cursor_row(), 7);
942
943    term.set_cursor_row(4);
944    term.set_cursor_col(4);
945    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947    term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949    term.cursor_right(5, false);
950    assert_eq!(term.cursor_col(), 9);
951    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953    // Insert two blank rows above cursor. Cursor stays put.
954    term.insert_rows(2);
955    assert_eq!(term.cursor_row(), 4);
956    assert_eq!(term.cursor_col(), 9);
957    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958    term.cursor_down(2, false); // Go down to find our text again
959    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961    // Go back to the beginning of the inserted 'x' characters and delete them.
962    term.cursor_left(5);
963    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964    term.delete_cur_chars(5);
965    assert_eq!(term.cursor_row(), 6);
966    assert_eq!(term.cursor_col(), 4);
967    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969    term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970    term.cursor_down(1, false);
971    term.cursor_left(2);
972    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974    term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975    assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976    term.cursor_up(1, false);
977    term.delete_rows(2); // Delete remains of test pattern
978
979    term.set_text_attrib(Attrib::Bold);
980    term.insert_char_eol('-', 3, 15, 20);
981    term.set_cursor_row(3);
982    term.set_cursor_col(15);
983    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984    assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986    term.set_text_attrib(Attrib::Italic);
987    term.append(" and all lines below");
988    term.set_text_attrib(Attrib::Normal);
989    term.cursor_down(1, false);
990
991    let mut hsb = term.hscrollbar();
992    let mut sb = term.scrollbar();
993    hsb.set_value(100.0);
994    assert_eq!(hsb.value(), 100.0);
995    sb.set_value(50.0);
996    assert_eq!(sb.value(), 50.0);
997    hsb.set_value(0.0);
998    assert_eq!(hsb.value(), 0.0);
999    sb.set_value(0.0);
1000    assert_eq!(sb.value(), 0.0);
1001}
Source

pub fn cursor_left(&mut self, count: i32)

Moves cursor left count columns, and cursor stops (does not wrap) if it hits screen edge. This is a low-level “protected” function of the fltk library

Examples found in repository?
examples/terminal.rs (line 893)
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700    term.take_focus().unwrap();
701
702    // Test the attr_fg_color and attr_bg_color methods.
703    // Put a single character 'A' into the buffer and check it
704    term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705    term.set_text_bg_color(Color::TransparentBg);
706    term.set_text_fg_color(Color::XtermWhite);
707    term.append("A");
708    let r = &term.u8c_disp_row(0);
709    let uc = r.col(0);
710    assert_eq!(uc.text_utf8(), b"A");
711    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712    assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713    assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714    assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715    assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717    // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718    term.clear();
719    term.set_text_fg_color_xterm(XtermColor::White);
720    term.set_text_bg_color_xterm(XtermColor::Black);
721    assert_eq!(term.text_attrib(), Attrib::Normal);
722
723    assert!(term.ansi());
724    term.append("B\x1b[32mC\x1b[1mD\n");
725
726    let r = &term.u8c_disp_row(0);
727    let uc = r.col(0);
728    assert_eq!(uc.text_utf8(), b"B");
729    assert!(uc.is_char(b'B'));
730    assert!(!uc.is_char(b'A'));
731    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735    assert_eq!(
736        &uc.charflags(),
737        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738    );
739
740    let uc = r.col(1);
741    assert_eq!(uc.text_utf8(), b"C");
742    assert!(uc.is_char(b'C'));
743    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747    assert_eq!(
748        &uc.charflags(),
749        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750    );
751
752    let uc = r.col(2);
753    assert_eq!(uc.text_utf8(), b"D");
754    assert!(uc.is_char(b'D'));
755    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757    assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759    assert_eq!(
760        &uc.attr_fgcolor(Some(term)),
761        &Color::from_rgb(0x20, 0xf0, 0x20)
762    );
763    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764    assert_eq!(
765        &uc.charflags(),
766        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767    );
768
769    // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770    term.clear();
771    term.set_text_fg_color_xterm(XtermColor::White);
772    term.set_text_bg_color_xterm(XtermColor::Black);
773    term.set_text_attrib(Attrib::Normal);
774    assert_eq!(term.text_attrib(), Attrib::Normal);
775
776    assert!(term.ansi());
777    term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779    let r = &term.u8c_disp_row(0);
780    let uc = r.col(0);
781    assert_eq!(uc.text_utf8(), b"B");
782    assert!(uc.is_char(b'B'));
783    assert!(!uc.is_char(b'A'));
784    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788    assert_eq!(
789        &uc.charflags(),
790        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791    );
792
793    let uc = r.col(1);
794    assert_eq!(uc.text_utf8(), b"C");
795    assert!(uc.is_char(b'C'));
796    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800    assert_eq!(
801        &uc.charflags(),
802        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803    );
804
805    let uc = r.col(2);
806    assert_eq!(uc.text_utf8(), b"D");
807    assert!(uc.is_char(b'D'));
808    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812    assert_eq!(
813        &uc.charflags(),
814        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815    );
816
817    let uc = r.col(3);
818    assert_eq!(uc.text_utf8(), b"E");
819    assert!(uc.is_char(b'E'));
820    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822    assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823    assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824    assert_eq!(
825        &uc.charflags(),
826        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827    );
828
829    // Test some miscellaneous Utf8 constants
830    assert_eq!(uc.length(), 1);
831    assert_eq!(uc.max_utf8(), 4);
832    assert_eq!(uc.pwidth(), 9.0);
833    assert_eq!(uc.pwidth_int(), 9);
834
835    term.set_text_fg_color_xterm(XtermColor::White);
836    term.set_text_bg_color_xterm(XtermColor::Black);
837    term.clear();
838    term.set_text_attrib(Attrib::Normal);
839
840    // Mouse selection functions
841    term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842    term.clear_mouse_selection();
843    assert_eq!(term.get_selection(), None);
844
845    // Play with cursor position
846    term.append("0123456789\n"); // Set up test pattern
847    term.append("ABCDEFGHIJ\n");
848    term.append("abcdefghij\n");
849
850    term.set_cursor_row(1);
851    assert_eq!(term.cursor_row(), 1);
852    term.set_cursor_col(1);
853    assert_eq!(term.cursor_col(), 1);
854    assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856    term.append("----"); // Overwrites text at cursor and moves cursor forward
857    assert_eq!(term.cursor_row(), 1);
858    assert_eq!(term.cursor_col(), 5);
859    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860    term.set_cursor_col(1);
861    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863    term.cursor_up(1, false);
864    assert_eq!(term.cursor_row(), 0);
865    assert_eq!(term.cursor_col(), 1);
866    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868    // Hit top of screen, so nothing happens
869    term.cursor_up(1, false);
870    assert_eq!(term.cursor_row(), 0);
871    assert_eq!(term.cursor_col(), 1);
872    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874    // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875    term.cursor_up(1, true);
876    assert_eq!(term.cursor_row(), 0);
877    assert_eq!(term.cursor_col(), 1);
878    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880    // Go back down to the overwritten text
881    term.cursor_down(2, false);
882    assert_eq!(term.cursor_row(), 2);
883    assert_eq!(term.cursor_col(), 1);
884    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886    // Go right past the overwritten text
887    term.cursor_right(4, false);
888    assert_eq!(term.cursor_row(), 2);
889    assert_eq!(term.cursor_col(), 5);
890    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892    // Go left to the end of the overwritten text
893    term.cursor_left(1);
894    assert_eq!(term.cursor_row(), 2);
895    assert_eq!(term.cursor_col(), 4);
896    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898    // Scroll back down, removing the blank line at the top.
899    // Cursor stays in place, the text moves under it.
900    term.scroll(1);
901    assert_eq!(term.cursor_row(), 2);
902    assert_eq!(term.cursor_col(), 4);
903    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905    // Clear from here to end-of-line
906    term.clear_eol();
907    assert_eq!(term.cursor_row(), 2);
908    assert_eq!(term.cursor_col(), 4);
909    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911    // Now clear from here to start-of-line. Cursor does not move.
912    term.clear_sol();
913    assert_eq!(term.cursor_row(), 2);
914    assert_eq!(term.cursor_col(), 4);
915    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916    term.cursor_left(1);
917    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918    term.set_cursor_col(0);
919    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921    // Clear some lines
922    term.clear_line(1);
923    assert_eq!(term.cursor_row(), 2);
924    assert_eq!(term.cursor_col(), 0);
925    term.set_cursor_row(1);
926    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927    term.set_cursor_row(3);
928    term.clear_cur_line();
929    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930    assert_eq!(term.cursor_row(), 3);
931    assert_eq!(term.cursor_col(), 0);
932
933    term.append("Two lines above are intentionally left blank.\n");
934    assert_eq!(term.cursor_row(), 4);
935    assert_eq!(term.cursor_col(), 0);
936
937    // Set up the test pattern again, then play with insert/delete
938    term.append("0123456789\n");
939    term.append("ABCDEFGHIJ\n");
940    term.append("abcdefghij\n");
941    assert_eq!(term.cursor_row(), 7);
942
943    term.set_cursor_row(4);
944    term.set_cursor_col(4);
945    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947    term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949    term.cursor_right(5, false);
950    assert_eq!(term.cursor_col(), 9);
951    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953    // Insert two blank rows above cursor. Cursor stays put.
954    term.insert_rows(2);
955    assert_eq!(term.cursor_row(), 4);
956    assert_eq!(term.cursor_col(), 9);
957    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958    term.cursor_down(2, false); // Go down to find our text again
959    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961    // Go back to the beginning of the inserted 'x' characters and delete them.
962    term.cursor_left(5);
963    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964    term.delete_cur_chars(5);
965    assert_eq!(term.cursor_row(), 6);
966    assert_eq!(term.cursor_col(), 4);
967    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969    term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970    term.cursor_down(1, false);
971    term.cursor_left(2);
972    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974    term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975    assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976    term.cursor_up(1, false);
977    term.delete_rows(2); // Delete remains of test pattern
978
979    term.set_text_attrib(Attrib::Bold);
980    term.insert_char_eol('-', 3, 15, 20);
981    term.set_cursor_row(3);
982    term.set_cursor_col(15);
983    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984    assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986    term.set_text_attrib(Attrib::Italic);
987    term.append(" and all lines below");
988    term.set_text_attrib(Attrib::Normal);
989    term.cursor_down(1, false);
990
991    let mut hsb = term.hscrollbar();
992    let mut sb = term.scrollbar();
993    hsb.set_value(100.0);
994    assert_eq!(hsb.value(), 100.0);
995    sb.set_value(50.0);
996    assert_eq!(sb.value(), 50.0);
997    hsb.set_value(0.0);
998    assert_eq!(hsb.value(), 0.0);
999    sb.set_value(0.0);
1000    assert_eq!(sb.value(), 0.0);
1001}
Source

pub fn cursor_right(&mut self, count: i32, do_scroll: bool)

Moves cursor right count columns. If cursor hits right edge of screen, it either stops (does not wrap) if do_scroll is false, or wraps and scrolls up one line if do_scroll is true. This is a low-level “protected” function of the fltk library

Examples found in repository?
examples/terminal.rs (line 887)
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700    term.take_focus().unwrap();
701
702    // Test the attr_fg_color and attr_bg_color methods.
703    // Put a single character 'A' into the buffer and check it
704    term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705    term.set_text_bg_color(Color::TransparentBg);
706    term.set_text_fg_color(Color::XtermWhite);
707    term.append("A");
708    let r = &term.u8c_disp_row(0);
709    let uc = r.col(0);
710    assert_eq!(uc.text_utf8(), b"A");
711    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712    assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713    assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714    assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715    assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717    // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718    term.clear();
719    term.set_text_fg_color_xterm(XtermColor::White);
720    term.set_text_bg_color_xterm(XtermColor::Black);
721    assert_eq!(term.text_attrib(), Attrib::Normal);
722
723    assert!(term.ansi());
724    term.append("B\x1b[32mC\x1b[1mD\n");
725
726    let r = &term.u8c_disp_row(0);
727    let uc = r.col(0);
728    assert_eq!(uc.text_utf8(), b"B");
729    assert!(uc.is_char(b'B'));
730    assert!(!uc.is_char(b'A'));
731    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735    assert_eq!(
736        &uc.charflags(),
737        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738    );
739
740    let uc = r.col(1);
741    assert_eq!(uc.text_utf8(), b"C");
742    assert!(uc.is_char(b'C'));
743    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747    assert_eq!(
748        &uc.charflags(),
749        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750    );
751
752    let uc = r.col(2);
753    assert_eq!(uc.text_utf8(), b"D");
754    assert!(uc.is_char(b'D'));
755    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757    assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759    assert_eq!(
760        &uc.attr_fgcolor(Some(term)),
761        &Color::from_rgb(0x20, 0xf0, 0x20)
762    );
763    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764    assert_eq!(
765        &uc.charflags(),
766        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767    );
768
769    // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770    term.clear();
771    term.set_text_fg_color_xterm(XtermColor::White);
772    term.set_text_bg_color_xterm(XtermColor::Black);
773    term.set_text_attrib(Attrib::Normal);
774    assert_eq!(term.text_attrib(), Attrib::Normal);
775
776    assert!(term.ansi());
777    term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779    let r = &term.u8c_disp_row(0);
780    let uc = r.col(0);
781    assert_eq!(uc.text_utf8(), b"B");
782    assert!(uc.is_char(b'B'));
783    assert!(!uc.is_char(b'A'));
784    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788    assert_eq!(
789        &uc.charflags(),
790        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791    );
792
793    let uc = r.col(1);
794    assert_eq!(uc.text_utf8(), b"C");
795    assert!(uc.is_char(b'C'));
796    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800    assert_eq!(
801        &uc.charflags(),
802        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803    );
804
805    let uc = r.col(2);
806    assert_eq!(uc.text_utf8(), b"D");
807    assert!(uc.is_char(b'D'));
808    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812    assert_eq!(
813        &uc.charflags(),
814        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815    );
816
817    let uc = r.col(3);
818    assert_eq!(uc.text_utf8(), b"E");
819    assert!(uc.is_char(b'E'));
820    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822    assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823    assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824    assert_eq!(
825        &uc.charflags(),
826        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827    );
828
829    // Test some miscellaneous Utf8 constants
830    assert_eq!(uc.length(), 1);
831    assert_eq!(uc.max_utf8(), 4);
832    assert_eq!(uc.pwidth(), 9.0);
833    assert_eq!(uc.pwidth_int(), 9);
834
835    term.set_text_fg_color_xterm(XtermColor::White);
836    term.set_text_bg_color_xterm(XtermColor::Black);
837    term.clear();
838    term.set_text_attrib(Attrib::Normal);
839
840    // Mouse selection functions
841    term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842    term.clear_mouse_selection();
843    assert_eq!(term.get_selection(), None);
844
845    // Play with cursor position
846    term.append("0123456789\n"); // Set up test pattern
847    term.append("ABCDEFGHIJ\n");
848    term.append("abcdefghij\n");
849
850    term.set_cursor_row(1);
851    assert_eq!(term.cursor_row(), 1);
852    term.set_cursor_col(1);
853    assert_eq!(term.cursor_col(), 1);
854    assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856    term.append("----"); // Overwrites text at cursor and moves cursor forward
857    assert_eq!(term.cursor_row(), 1);
858    assert_eq!(term.cursor_col(), 5);
859    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860    term.set_cursor_col(1);
861    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863    term.cursor_up(1, false);
864    assert_eq!(term.cursor_row(), 0);
865    assert_eq!(term.cursor_col(), 1);
866    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868    // Hit top of screen, so nothing happens
869    term.cursor_up(1, false);
870    assert_eq!(term.cursor_row(), 0);
871    assert_eq!(term.cursor_col(), 1);
872    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874    // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875    term.cursor_up(1, true);
876    assert_eq!(term.cursor_row(), 0);
877    assert_eq!(term.cursor_col(), 1);
878    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880    // Go back down to the overwritten text
881    term.cursor_down(2, false);
882    assert_eq!(term.cursor_row(), 2);
883    assert_eq!(term.cursor_col(), 1);
884    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886    // Go right past the overwritten text
887    term.cursor_right(4, false);
888    assert_eq!(term.cursor_row(), 2);
889    assert_eq!(term.cursor_col(), 5);
890    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892    // Go left to the end of the overwritten text
893    term.cursor_left(1);
894    assert_eq!(term.cursor_row(), 2);
895    assert_eq!(term.cursor_col(), 4);
896    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898    // Scroll back down, removing the blank line at the top.
899    // Cursor stays in place, the text moves under it.
900    term.scroll(1);
901    assert_eq!(term.cursor_row(), 2);
902    assert_eq!(term.cursor_col(), 4);
903    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905    // Clear from here to end-of-line
906    term.clear_eol();
907    assert_eq!(term.cursor_row(), 2);
908    assert_eq!(term.cursor_col(), 4);
909    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911    // Now clear from here to start-of-line. Cursor does not move.
912    term.clear_sol();
913    assert_eq!(term.cursor_row(), 2);
914    assert_eq!(term.cursor_col(), 4);
915    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916    term.cursor_left(1);
917    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918    term.set_cursor_col(0);
919    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921    // Clear some lines
922    term.clear_line(1);
923    assert_eq!(term.cursor_row(), 2);
924    assert_eq!(term.cursor_col(), 0);
925    term.set_cursor_row(1);
926    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927    term.set_cursor_row(3);
928    term.clear_cur_line();
929    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930    assert_eq!(term.cursor_row(), 3);
931    assert_eq!(term.cursor_col(), 0);
932
933    term.append("Two lines above are intentionally left blank.\n");
934    assert_eq!(term.cursor_row(), 4);
935    assert_eq!(term.cursor_col(), 0);
936
937    // Set up the test pattern again, then play with insert/delete
938    term.append("0123456789\n");
939    term.append("ABCDEFGHIJ\n");
940    term.append("abcdefghij\n");
941    assert_eq!(term.cursor_row(), 7);
942
943    term.set_cursor_row(4);
944    term.set_cursor_col(4);
945    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947    term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949    term.cursor_right(5, false);
950    assert_eq!(term.cursor_col(), 9);
951    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953    // Insert two blank rows above cursor. Cursor stays put.
954    term.insert_rows(2);
955    assert_eq!(term.cursor_row(), 4);
956    assert_eq!(term.cursor_col(), 9);
957    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958    term.cursor_down(2, false); // Go down to find our text again
959    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961    // Go back to the beginning of the inserted 'x' characters and delete them.
962    term.cursor_left(5);
963    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964    term.delete_cur_chars(5);
965    assert_eq!(term.cursor_row(), 6);
966    assert_eq!(term.cursor_col(), 4);
967    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969    term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970    term.cursor_down(1, false);
971    term.cursor_left(2);
972    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974    term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975    assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976    term.cursor_up(1, false);
977    term.delete_rows(2); // Delete remains of test pattern
978
979    term.set_text_attrib(Attrib::Bold);
980    term.insert_char_eol('-', 3, 15, 20);
981    term.set_cursor_row(3);
982    term.set_cursor_col(15);
983    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984    assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986    term.set_text_attrib(Attrib::Italic);
987    term.append(" and all lines below");
988    term.set_text_attrib(Attrib::Normal);
989    term.cursor_down(1, false);
990
991    let mut hsb = term.hscrollbar();
992    let mut sb = term.scrollbar();
993    hsb.set_value(100.0);
994    assert_eq!(hsb.value(), 100.0);
995    sb.set_value(50.0);
996    assert_eq!(sb.value(), 50.0);
997    hsb.set_value(0.0);
998    assert_eq!(hsb.value(), 0.0);
999    sb.set_value(0.0);
1000    assert_eq!(sb.value(), 0.0);
1001}
Source

pub fn scroll(&mut self, count: i32)

Scroll the selection up(+)/down(-) number of rows This is a low-level “protected” function of the fltk library

Examples found in repository?
examples/terminal.rs (line 900)
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700    term.take_focus().unwrap();
701
702    // Test the attr_fg_color and attr_bg_color methods.
703    // Put a single character 'A' into the buffer and check it
704    term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705    term.set_text_bg_color(Color::TransparentBg);
706    term.set_text_fg_color(Color::XtermWhite);
707    term.append("A");
708    let r = &term.u8c_disp_row(0);
709    let uc = r.col(0);
710    assert_eq!(uc.text_utf8(), b"A");
711    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712    assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713    assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714    assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715    assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717    // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718    term.clear();
719    term.set_text_fg_color_xterm(XtermColor::White);
720    term.set_text_bg_color_xterm(XtermColor::Black);
721    assert_eq!(term.text_attrib(), Attrib::Normal);
722
723    assert!(term.ansi());
724    term.append("B\x1b[32mC\x1b[1mD\n");
725
726    let r = &term.u8c_disp_row(0);
727    let uc = r.col(0);
728    assert_eq!(uc.text_utf8(), b"B");
729    assert!(uc.is_char(b'B'));
730    assert!(!uc.is_char(b'A'));
731    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735    assert_eq!(
736        &uc.charflags(),
737        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738    );
739
740    let uc = r.col(1);
741    assert_eq!(uc.text_utf8(), b"C");
742    assert!(uc.is_char(b'C'));
743    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747    assert_eq!(
748        &uc.charflags(),
749        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750    );
751
752    let uc = r.col(2);
753    assert_eq!(uc.text_utf8(), b"D");
754    assert!(uc.is_char(b'D'));
755    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757    assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759    assert_eq!(
760        &uc.attr_fgcolor(Some(term)),
761        &Color::from_rgb(0x20, 0xf0, 0x20)
762    );
763    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764    assert_eq!(
765        &uc.charflags(),
766        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767    );
768
769    // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770    term.clear();
771    term.set_text_fg_color_xterm(XtermColor::White);
772    term.set_text_bg_color_xterm(XtermColor::Black);
773    term.set_text_attrib(Attrib::Normal);
774    assert_eq!(term.text_attrib(), Attrib::Normal);
775
776    assert!(term.ansi());
777    term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779    let r = &term.u8c_disp_row(0);
780    let uc = r.col(0);
781    assert_eq!(uc.text_utf8(), b"B");
782    assert!(uc.is_char(b'B'));
783    assert!(!uc.is_char(b'A'));
784    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788    assert_eq!(
789        &uc.charflags(),
790        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791    );
792
793    let uc = r.col(1);
794    assert_eq!(uc.text_utf8(), b"C");
795    assert!(uc.is_char(b'C'));
796    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800    assert_eq!(
801        &uc.charflags(),
802        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803    );
804
805    let uc = r.col(2);
806    assert_eq!(uc.text_utf8(), b"D");
807    assert!(uc.is_char(b'D'));
808    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812    assert_eq!(
813        &uc.charflags(),
814        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815    );
816
817    let uc = r.col(3);
818    assert_eq!(uc.text_utf8(), b"E");
819    assert!(uc.is_char(b'E'));
820    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822    assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823    assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824    assert_eq!(
825        &uc.charflags(),
826        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827    );
828
829    // Test some miscellaneous Utf8 constants
830    assert_eq!(uc.length(), 1);
831    assert_eq!(uc.max_utf8(), 4);
832    assert_eq!(uc.pwidth(), 9.0);
833    assert_eq!(uc.pwidth_int(), 9);
834
835    term.set_text_fg_color_xterm(XtermColor::White);
836    term.set_text_bg_color_xterm(XtermColor::Black);
837    term.clear();
838    term.set_text_attrib(Attrib::Normal);
839
840    // Mouse selection functions
841    term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842    term.clear_mouse_selection();
843    assert_eq!(term.get_selection(), None);
844
845    // Play with cursor position
846    term.append("0123456789\n"); // Set up test pattern
847    term.append("ABCDEFGHIJ\n");
848    term.append("abcdefghij\n");
849
850    term.set_cursor_row(1);
851    assert_eq!(term.cursor_row(), 1);
852    term.set_cursor_col(1);
853    assert_eq!(term.cursor_col(), 1);
854    assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856    term.append("----"); // Overwrites text at cursor and moves cursor forward
857    assert_eq!(term.cursor_row(), 1);
858    assert_eq!(term.cursor_col(), 5);
859    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860    term.set_cursor_col(1);
861    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863    term.cursor_up(1, false);
864    assert_eq!(term.cursor_row(), 0);
865    assert_eq!(term.cursor_col(), 1);
866    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868    // Hit top of screen, so nothing happens
869    term.cursor_up(1, false);
870    assert_eq!(term.cursor_row(), 0);
871    assert_eq!(term.cursor_col(), 1);
872    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874    // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875    term.cursor_up(1, true);
876    assert_eq!(term.cursor_row(), 0);
877    assert_eq!(term.cursor_col(), 1);
878    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880    // Go back down to the overwritten text
881    term.cursor_down(2, false);
882    assert_eq!(term.cursor_row(), 2);
883    assert_eq!(term.cursor_col(), 1);
884    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886    // Go right past the overwritten text
887    term.cursor_right(4, false);
888    assert_eq!(term.cursor_row(), 2);
889    assert_eq!(term.cursor_col(), 5);
890    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892    // Go left to the end of the overwritten text
893    term.cursor_left(1);
894    assert_eq!(term.cursor_row(), 2);
895    assert_eq!(term.cursor_col(), 4);
896    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898    // Scroll back down, removing the blank line at the top.
899    // Cursor stays in place, the text moves under it.
900    term.scroll(1);
901    assert_eq!(term.cursor_row(), 2);
902    assert_eq!(term.cursor_col(), 4);
903    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905    // Clear from here to end-of-line
906    term.clear_eol();
907    assert_eq!(term.cursor_row(), 2);
908    assert_eq!(term.cursor_col(), 4);
909    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911    // Now clear from here to start-of-line. Cursor does not move.
912    term.clear_sol();
913    assert_eq!(term.cursor_row(), 2);
914    assert_eq!(term.cursor_col(), 4);
915    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916    term.cursor_left(1);
917    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918    term.set_cursor_col(0);
919    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921    // Clear some lines
922    term.clear_line(1);
923    assert_eq!(term.cursor_row(), 2);
924    assert_eq!(term.cursor_col(), 0);
925    term.set_cursor_row(1);
926    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927    term.set_cursor_row(3);
928    term.clear_cur_line();
929    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930    assert_eq!(term.cursor_row(), 3);
931    assert_eq!(term.cursor_col(), 0);
932
933    term.append("Two lines above are intentionally left blank.\n");
934    assert_eq!(term.cursor_row(), 4);
935    assert_eq!(term.cursor_col(), 0);
936
937    // Set up the test pattern again, then play with insert/delete
938    term.append("0123456789\n");
939    term.append("ABCDEFGHIJ\n");
940    term.append("abcdefghij\n");
941    assert_eq!(term.cursor_row(), 7);
942
943    term.set_cursor_row(4);
944    term.set_cursor_col(4);
945    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947    term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949    term.cursor_right(5, false);
950    assert_eq!(term.cursor_col(), 9);
951    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953    // Insert two blank rows above cursor. Cursor stays put.
954    term.insert_rows(2);
955    assert_eq!(term.cursor_row(), 4);
956    assert_eq!(term.cursor_col(), 9);
957    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958    term.cursor_down(2, false); // Go down to find our text again
959    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961    // Go back to the beginning of the inserted 'x' characters and delete them.
962    term.cursor_left(5);
963    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964    term.delete_cur_chars(5);
965    assert_eq!(term.cursor_row(), 6);
966    assert_eq!(term.cursor_col(), 4);
967    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969    term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970    term.cursor_down(1, false);
971    term.cursor_left(2);
972    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974    term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975    assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976    term.cursor_up(1, false);
977    term.delete_rows(2); // Delete remains of test pattern
978
979    term.set_text_attrib(Attrib::Bold);
980    term.insert_char_eol('-', 3, 15, 20);
981    term.set_cursor_row(3);
982    term.set_cursor_col(15);
983    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984    assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986    term.set_text_attrib(Attrib::Italic);
987    term.append(" and all lines below");
988    term.set_text_attrib(Attrib::Normal);
989    term.cursor_down(1, false);
990
991    let mut hsb = term.hscrollbar();
992    let mut sb = term.scrollbar();
993    hsb.set_value(100.0);
994    assert_eq!(hsb.value(), 100.0);
995    sb.set_value(50.0);
996    assert_eq!(sb.value(), 50.0);
997    hsb.set_value(0.0);
998    assert_eq!(hsb.value(), 0.0);
999    sb.set_value(0.0);
1000    assert_eq!(sb.value(), 0.0);
1001}
Source

pub fn clear_eod(&mut self)

Clear from cursor to End Of Display (EOD), like “<ESC>[J<ESC>[0J”.

Source

pub fn clear_eol(&mut self)

Clear from cursor to End Of Line (EOL), like “<ESC>[K”.

Examples found in repository?
examples/terminal.rs (line 906)
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700    term.take_focus().unwrap();
701
702    // Test the attr_fg_color and attr_bg_color methods.
703    // Put a single character 'A' into the buffer and check it
704    term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705    term.set_text_bg_color(Color::TransparentBg);
706    term.set_text_fg_color(Color::XtermWhite);
707    term.append("A");
708    let r = &term.u8c_disp_row(0);
709    let uc = r.col(0);
710    assert_eq!(uc.text_utf8(), b"A");
711    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712    assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713    assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714    assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715    assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717    // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718    term.clear();
719    term.set_text_fg_color_xterm(XtermColor::White);
720    term.set_text_bg_color_xterm(XtermColor::Black);
721    assert_eq!(term.text_attrib(), Attrib::Normal);
722
723    assert!(term.ansi());
724    term.append("B\x1b[32mC\x1b[1mD\n");
725
726    let r = &term.u8c_disp_row(0);
727    let uc = r.col(0);
728    assert_eq!(uc.text_utf8(), b"B");
729    assert!(uc.is_char(b'B'));
730    assert!(!uc.is_char(b'A'));
731    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735    assert_eq!(
736        &uc.charflags(),
737        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738    );
739
740    let uc = r.col(1);
741    assert_eq!(uc.text_utf8(), b"C");
742    assert!(uc.is_char(b'C'));
743    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747    assert_eq!(
748        &uc.charflags(),
749        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750    );
751
752    let uc = r.col(2);
753    assert_eq!(uc.text_utf8(), b"D");
754    assert!(uc.is_char(b'D'));
755    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757    assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759    assert_eq!(
760        &uc.attr_fgcolor(Some(term)),
761        &Color::from_rgb(0x20, 0xf0, 0x20)
762    );
763    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764    assert_eq!(
765        &uc.charflags(),
766        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767    );
768
769    // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770    term.clear();
771    term.set_text_fg_color_xterm(XtermColor::White);
772    term.set_text_bg_color_xterm(XtermColor::Black);
773    term.set_text_attrib(Attrib::Normal);
774    assert_eq!(term.text_attrib(), Attrib::Normal);
775
776    assert!(term.ansi());
777    term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779    let r = &term.u8c_disp_row(0);
780    let uc = r.col(0);
781    assert_eq!(uc.text_utf8(), b"B");
782    assert!(uc.is_char(b'B'));
783    assert!(!uc.is_char(b'A'));
784    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788    assert_eq!(
789        &uc.charflags(),
790        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791    );
792
793    let uc = r.col(1);
794    assert_eq!(uc.text_utf8(), b"C");
795    assert!(uc.is_char(b'C'));
796    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800    assert_eq!(
801        &uc.charflags(),
802        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803    );
804
805    let uc = r.col(2);
806    assert_eq!(uc.text_utf8(), b"D");
807    assert!(uc.is_char(b'D'));
808    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812    assert_eq!(
813        &uc.charflags(),
814        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815    );
816
817    let uc = r.col(3);
818    assert_eq!(uc.text_utf8(), b"E");
819    assert!(uc.is_char(b'E'));
820    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822    assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823    assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824    assert_eq!(
825        &uc.charflags(),
826        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827    );
828
829    // Test some miscellaneous Utf8 constants
830    assert_eq!(uc.length(), 1);
831    assert_eq!(uc.max_utf8(), 4);
832    assert_eq!(uc.pwidth(), 9.0);
833    assert_eq!(uc.pwidth_int(), 9);
834
835    term.set_text_fg_color_xterm(XtermColor::White);
836    term.set_text_bg_color_xterm(XtermColor::Black);
837    term.clear();
838    term.set_text_attrib(Attrib::Normal);
839
840    // Mouse selection functions
841    term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842    term.clear_mouse_selection();
843    assert_eq!(term.get_selection(), None);
844
845    // Play with cursor position
846    term.append("0123456789\n"); // Set up test pattern
847    term.append("ABCDEFGHIJ\n");
848    term.append("abcdefghij\n");
849
850    term.set_cursor_row(1);
851    assert_eq!(term.cursor_row(), 1);
852    term.set_cursor_col(1);
853    assert_eq!(term.cursor_col(), 1);
854    assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856    term.append("----"); // Overwrites text at cursor and moves cursor forward
857    assert_eq!(term.cursor_row(), 1);
858    assert_eq!(term.cursor_col(), 5);
859    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860    term.set_cursor_col(1);
861    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863    term.cursor_up(1, false);
864    assert_eq!(term.cursor_row(), 0);
865    assert_eq!(term.cursor_col(), 1);
866    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868    // Hit top of screen, so nothing happens
869    term.cursor_up(1, false);
870    assert_eq!(term.cursor_row(), 0);
871    assert_eq!(term.cursor_col(), 1);
872    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874    // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875    term.cursor_up(1, true);
876    assert_eq!(term.cursor_row(), 0);
877    assert_eq!(term.cursor_col(), 1);
878    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880    // Go back down to the overwritten text
881    term.cursor_down(2, false);
882    assert_eq!(term.cursor_row(), 2);
883    assert_eq!(term.cursor_col(), 1);
884    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886    // Go right past the overwritten text
887    term.cursor_right(4, false);
888    assert_eq!(term.cursor_row(), 2);
889    assert_eq!(term.cursor_col(), 5);
890    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892    // Go left to the end of the overwritten text
893    term.cursor_left(1);
894    assert_eq!(term.cursor_row(), 2);
895    assert_eq!(term.cursor_col(), 4);
896    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898    // Scroll back down, removing the blank line at the top.
899    // Cursor stays in place, the text moves under it.
900    term.scroll(1);
901    assert_eq!(term.cursor_row(), 2);
902    assert_eq!(term.cursor_col(), 4);
903    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905    // Clear from here to end-of-line
906    term.clear_eol();
907    assert_eq!(term.cursor_row(), 2);
908    assert_eq!(term.cursor_col(), 4);
909    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911    // Now clear from here to start-of-line. Cursor does not move.
912    term.clear_sol();
913    assert_eq!(term.cursor_row(), 2);
914    assert_eq!(term.cursor_col(), 4);
915    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916    term.cursor_left(1);
917    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918    term.set_cursor_col(0);
919    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921    // Clear some lines
922    term.clear_line(1);
923    assert_eq!(term.cursor_row(), 2);
924    assert_eq!(term.cursor_col(), 0);
925    term.set_cursor_row(1);
926    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927    term.set_cursor_row(3);
928    term.clear_cur_line();
929    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930    assert_eq!(term.cursor_row(), 3);
931    assert_eq!(term.cursor_col(), 0);
932
933    term.append("Two lines above are intentionally left blank.\n");
934    assert_eq!(term.cursor_row(), 4);
935    assert_eq!(term.cursor_col(), 0);
936
937    // Set up the test pattern again, then play with insert/delete
938    term.append("0123456789\n");
939    term.append("ABCDEFGHIJ\n");
940    term.append("abcdefghij\n");
941    assert_eq!(term.cursor_row(), 7);
942
943    term.set_cursor_row(4);
944    term.set_cursor_col(4);
945    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947    term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949    term.cursor_right(5, false);
950    assert_eq!(term.cursor_col(), 9);
951    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953    // Insert two blank rows above cursor. Cursor stays put.
954    term.insert_rows(2);
955    assert_eq!(term.cursor_row(), 4);
956    assert_eq!(term.cursor_col(), 9);
957    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958    term.cursor_down(2, false); // Go down to find our text again
959    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961    // Go back to the beginning of the inserted 'x' characters and delete them.
962    term.cursor_left(5);
963    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964    term.delete_cur_chars(5);
965    assert_eq!(term.cursor_row(), 6);
966    assert_eq!(term.cursor_col(), 4);
967    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969    term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970    term.cursor_down(1, false);
971    term.cursor_left(2);
972    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974    term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975    assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976    term.cursor_up(1, false);
977    term.delete_rows(2); // Delete remains of test pattern
978
979    term.set_text_attrib(Attrib::Bold);
980    term.insert_char_eol('-', 3, 15, 20);
981    term.set_cursor_row(3);
982    term.set_cursor_col(15);
983    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984    assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986    term.set_text_attrib(Attrib::Italic);
987    term.append(" and all lines below");
988    term.set_text_attrib(Attrib::Normal);
989    term.cursor_down(1, false);
990
991    let mut hsb = term.hscrollbar();
992    let mut sb = term.scrollbar();
993    hsb.set_value(100.0);
994    assert_eq!(hsb.value(), 100.0);
995    sb.set_value(50.0);
996    assert_eq!(sb.value(), 50.0);
997    hsb.set_value(0.0);
998    assert_eq!(hsb.value(), 0.0);
999    sb.set_value(0.0);
1000    assert_eq!(sb.value(), 0.0);
1001}
Source

pub fn clear_cur_line(&mut self)

Clear entire line cursor is currently on.

Examples found in repository?
examples/terminal.rs (line 928)
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700    term.take_focus().unwrap();
701
702    // Test the attr_fg_color and attr_bg_color methods.
703    // Put a single character 'A' into the buffer and check it
704    term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705    term.set_text_bg_color(Color::TransparentBg);
706    term.set_text_fg_color(Color::XtermWhite);
707    term.append("A");
708    let r = &term.u8c_disp_row(0);
709    let uc = r.col(0);
710    assert_eq!(uc.text_utf8(), b"A");
711    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712    assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713    assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714    assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715    assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717    // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718    term.clear();
719    term.set_text_fg_color_xterm(XtermColor::White);
720    term.set_text_bg_color_xterm(XtermColor::Black);
721    assert_eq!(term.text_attrib(), Attrib::Normal);
722
723    assert!(term.ansi());
724    term.append("B\x1b[32mC\x1b[1mD\n");
725
726    let r = &term.u8c_disp_row(0);
727    let uc = r.col(0);
728    assert_eq!(uc.text_utf8(), b"B");
729    assert!(uc.is_char(b'B'));
730    assert!(!uc.is_char(b'A'));
731    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735    assert_eq!(
736        &uc.charflags(),
737        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738    );
739
740    let uc = r.col(1);
741    assert_eq!(uc.text_utf8(), b"C");
742    assert!(uc.is_char(b'C'));
743    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747    assert_eq!(
748        &uc.charflags(),
749        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750    );
751
752    let uc = r.col(2);
753    assert_eq!(uc.text_utf8(), b"D");
754    assert!(uc.is_char(b'D'));
755    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757    assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759    assert_eq!(
760        &uc.attr_fgcolor(Some(term)),
761        &Color::from_rgb(0x20, 0xf0, 0x20)
762    );
763    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764    assert_eq!(
765        &uc.charflags(),
766        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767    );
768
769    // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770    term.clear();
771    term.set_text_fg_color_xterm(XtermColor::White);
772    term.set_text_bg_color_xterm(XtermColor::Black);
773    term.set_text_attrib(Attrib::Normal);
774    assert_eq!(term.text_attrib(), Attrib::Normal);
775
776    assert!(term.ansi());
777    term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779    let r = &term.u8c_disp_row(0);
780    let uc = r.col(0);
781    assert_eq!(uc.text_utf8(), b"B");
782    assert!(uc.is_char(b'B'));
783    assert!(!uc.is_char(b'A'));
784    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788    assert_eq!(
789        &uc.charflags(),
790        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791    );
792
793    let uc = r.col(1);
794    assert_eq!(uc.text_utf8(), b"C");
795    assert!(uc.is_char(b'C'));
796    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800    assert_eq!(
801        &uc.charflags(),
802        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803    );
804
805    let uc = r.col(2);
806    assert_eq!(uc.text_utf8(), b"D");
807    assert!(uc.is_char(b'D'));
808    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812    assert_eq!(
813        &uc.charflags(),
814        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815    );
816
817    let uc = r.col(3);
818    assert_eq!(uc.text_utf8(), b"E");
819    assert!(uc.is_char(b'E'));
820    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822    assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823    assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824    assert_eq!(
825        &uc.charflags(),
826        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827    );
828
829    // Test some miscellaneous Utf8 constants
830    assert_eq!(uc.length(), 1);
831    assert_eq!(uc.max_utf8(), 4);
832    assert_eq!(uc.pwidth(), 9.0);
833    assert_eq!(uc.pwidth_int(), 9);
834
835    term.set_text_fg_color_xterm(XtermColor::White);
836    term.set_text_bg_color_xterm(XtermColor::Black);
837    term.clear();
838    term.set_text_attrib(Attrib::Normal);
839
840    // Mouse selection functions
841    term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842    term.clear_mouse_selection();
843    assert_eq!(term.get_selection(), None);
844
845    // Play with cursor position
846    term.append("0123456789\n"); // Set up test pattern
847    term.append("ABCDEFGHIJ\n");
848    term.append("abcdefghij\n");
849
850    term.set_cursor_row(1);
851    assert_eq!(term.cursor_row(), 1);
852    term.set_cursor_col(1);
853    assert_eq!(term.cursor_col(), 1);
854    assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856    term.append("----"); // Overwrites text at cursor and moves cursor forward
857    assert_eq!(term.cursor_row(), 1);
858    assert_eq!(term.cursor_col(), 5);
859    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860    term.set_cursor_col(1);
861    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863    term.cursor_up(1, false);
864    assert_eq!(term.cursor_row(), 0);
865    assert_eq!(term.cursor_col(), 1);
866    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868    // Hit top of screen, so nothing happens
869    term.cursor_up(1, false);
870    assert_eq!(term.cursor_row(), 0);
871    assert_eq!(term.cursor_col(), 1);
872    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874    // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875    term.cursor_up(1, true);
876    assert_eq!(term.cursor_row(), 0);
877    assert_eq!(term.cursor_col(), 1);
878    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880    // Go back down to the overwritten text
881    term.cursor_down(2, false);
882    assert_eq!(term.cursor_row(), 2);
883    assert_eq!(term.cursor_col(), 1);
884    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886    // Go right past the overwritten text
887    term.cursor_right(4, false);
888    assert_eq!(term.cursor_row(), 2);
889    assert_eq!(term.cursor_col(), 5);
890    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892    // Go left to the end of the overwritten text
893    term.cursor_left(1);
894    assert_eq!(term.cursor_row(), 2);
895    assert_eq!(term.cursor_col(), 4);
896    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898    // Scroll back down, removing the blank line at the top.
899    // Cursor stays in place, the text moves under it.
900    term.scroll(1);
901    assert_eq!(term.cursor_row(), 2);
902    assert_eq!(term.cursor_col(), 4);
903    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905    // Clear from here to end-of-line
906    term.clear_eol();
907    assert_eq!(term.cursor_row(), 2);
908    assert_eq!(term.cursor_col(), 4);
909    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911    // Now clear from here to start-of-line. Cursor does not move.
912    term.clear_sol();
913    assert_eq!(term.cursor_row(), 2);
914    assert_eq!(term.cursor_col(), 4);
915    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916    term.cursor_left(1);
917    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918    term.set_cursor_col(0);
919    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921    // Clear some lines
922    term.clear_line(1);
923    assert_eq!(term.cursor_row(), 2);
924    assert_eq!(term.cursor_col(), 0);
925    term.set_cursor_row(1);
926    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927    term.set_cursor_row(3);
928    term.clear_cur_line();
929    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930    assert_eq!(term.cursor_row(), 3);
931    assert_eq!(term.cursor_col(), 0);
932
933    term.append("Two lines above are intentionally left blank.\n");
934    assert_eq!(term.cursor_row(), 4);
935    assert_eq!(term.cursor_col(), 0);
936
937    // Set up the test pattern again, then play with insert/delete
938    term.append("0123456789\n");
939    term.append("ABCDEFGHIJ\n");
940    term.append("abcdefghij\n");
941    assert_eq!(term.cursor_row(), 7);
942
943    term.set_cursor_row(4);
944    term.set_cursor_col(4);
945    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947    term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949    term.cursor_right(5, false);
950    assert_eq!(term.cursor_col(), 9);
951    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953    // Insert two blank rows above cursor. Cursor stays put.
954    term.insert_rows(2);
955    assert_eq!(term.cursor_row(), 4);
956    assert_eq!(term.cursor_col(), 9);
957    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958    term.cursor_down(2, false); // Go down to find our text again
959    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961    // Go back to the beginning of the inserted 'x' characters and delete them.
962    term.cursor_left(5);
963    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964    term.delete_cur_chars(5);
965    assert_eq!(term.cursor_row(), 6);
966    assert_eq!(term.cursor_col(), 4);
967    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969    term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970    term.cursor_down(1, false);
971    term.cursor_left(2);
972    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974    term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975    assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976    term.cursor_up(1, false);
977    term.delete_rows(2); // Delete remains of test pattern
978
979    term.set_text_attrib(Attrib::Bold);
980    term.insert_char_eol('-', 3, 15, 20);
981    term.set_cursor_row(3);
982    term.set_cursor_col(15);
983    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984    assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986    term.set_text_attrib(Attrib::Italic);
987    term.append(" and all lines below");
988    term.set_text_attrib(Attrib::Normal);
989    term.cursor_down(1, false);
990
991    let mut hsb = term.hscrollbar();
992    let mut sb = term.scrollbar();
993    hsb.set_value(100.0);
994    assert_eq!(hsb.value(), 100.0);
995    sb.set_value(50.0);
996    assert_eq!(sb.value(), 50.0);
997    hsb.set_value(0.0);
998    assert_eq!(hsb.value(), 0.0);
999    sb.set_value(0.0);
1000    assert_eq!(sb.value(), 0.0);
1001}
Source

pub fn clear_line(&mut self, drow: i32)

Clear entire line for specified row.

Examples found in repository?
examples/terminal.rs (line 922)
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700    term.take_focus().unwrap();
701
702    // Test the attr_fg_color and attr_bg_color methods.
703    // Put a single character 'A' into the buffer and check it
704    term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705    term.set_text_bg_color(Color::TransparentBg);
706    term.set_text_fg_color(Color::XtermWhite);
707    term.append("A");
708    let r = &term.u8c_disp_row(0);
709    let uc = r.col(0);
710    assert_eq!(uc.text_utf8(), b"A");
711    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712    assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713    assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714    assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715    assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717    // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718    term.clear();
719    term.set_text_fg_color_xterm(XtermColor::White);
720    term.set_text_bg_color_xterm(XtermColor::Black);
721    assert_eq!(term.text_attrib(), Attrib::Normal);
722
723    assert!(term.ansi());
724    term.append("B\x1b[32mC\x1b[1mD\n");
725
726    let r = &term.u8c_disp_row(0);
727    let uc = r.col(0);
728    assert_eq!(uc.text_utf8(), b"B");
729    assert!(uc.is_char(b'B'));
730    assert!(!uc.is_char(b'A'));
731    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735    assert_eq!(
736        &uc.charflags(),
737        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738    );
739
740    let uc = r.col(1);
741    assert_eq!(uc.text_utf8(), b"C");
742    assert!(uc.is_char(b'C'));
743    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747    assert_eq!(
748        &uc.charflags(),
749        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750    );
751
752    let uc = r.col(2);
753    assert_eq!(uc.text_utf8(), b"D");
754    assert!(uc.is_char(b'D'));
755    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757    assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759    assert_eq!(
760        &uc.attr_fgcolor(Some(term)),
761        &Color::from_rgb(0x20, 0xf0, 0x20)
762    );
763    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764    assert_eq!(
765        &uc.charflags(),
766        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767    );
768
769    // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770    term.clear();
771    term.set_text_fg_color_xterm(XtermColor::White);
772    term.set_text_bg_color_xterm(XtermColor::Black);
773    term.set_text_attrib(Attrib::Normal);
774    assert_eq!(term.text_attrib(), Attrib::Normal);
775
776    assert!(term.ansi());
777    term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779    let r = &term.u8c_disp_row(0);
780    let uc = r.col(0);
781    assert_eq!(uc.text_utf8(), b"B");
782    assert!(uc.is_char(b'B'));
783    assert!(!uc.is_char(b'A'));
784    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788    assert_eq!(
789        &uc.charflags(),
790        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791    );
792
793    let uc = r.col(1);
794    assert_eq!(uc.text_utf8(), b"C");
795    assert!(uc.is_char(b'C'));
796    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800    assert_eq!(
801        &uc.charflags(),
802        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803    );
804
805    let uc = r.col(2);
806    assert_eq!(uc.text_utf8(), b"D");
807    assert!(uc.is_char(b'D'));
808    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812    assert_eq!(
813        &uc.charflags(),
814        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815    );
816
817    let uc = r.col(3);
818    assert_eq!(uc.text_utf8(), b"E");
819    assert!(uc.is_char(b'E'));
820    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822    assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823    assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824    assert_eq!(
825        &uc.charflags(),
826        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827    );
828
829    // Test some miscellaneous Utf8 constants
830    assert_eq!(uc.length(), 1);
831    assert_eq!(uc.max_utf8(), 4);
832    assert_eq!(uc.pwidth(), 9.0);
833    assert_eq!(uc.pwidth_int(), 9);
834
835    term.set_text_fg_color_xterm(XtermColor::White);
836    term.set_text_bg_color_xterm(XtermColor::Black);
837    term.clear();
838    term.set_text_attrib(Attrib::Normal);
839
840    // Mouse selection functions
841    term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842    term.clear_mouse_selection();
843    assert_eq!(term.get_selection(), None);
844
845    // Play with cursor position
846    term.append("0123456789\n"); // Set up test pattern
847    term.append("ABCDEFGHIJ\n");
848    term.append("abcdefghij\n");
849
850    term.set_cursor_row(1);
851    assert_eq!(term.cursor_row(), 1);
852    term.set_cursor_col(1);
853    assert_eq!(term.cursor_col(), 1);
854    assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856    term.append("----"); // Overwrites text at cursor and moves cursor forward
857    assert_eq!(term.cursor_row(), 1);
858    assert_eq!(term.cursor_col(), 5);
859    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860    term.set_cursor_col(1);
861    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863    term.cursor_up(1, false);
864    assert_eq!(term.cursor_row(), 0);
865    assert_eq!(term.cursor_col(), 1);
866    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868    // Hit top of screen, so nothing happens
869    term.cursor_up(1, false);
870    assert_eq!(term.cursor_row(), 0);
871    assert_eq!(term.cursor_col(), 1);
872    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874    // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875    term.cursor_up(1, true);
876    assert_eq!(term.cursor_row(), 0);
877    assert_eq!(term.cursor_col(), 1);
878    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880    // Go back down to the overwritten text
881    term.cursor_down(2, false);
882    assert_eq!(term.cursor_row(), 2);
883    assert_eq!(term.cursor_col(), 1);
884    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886    // Go right past the overwritten text
887    term.cursor_right(4, false);
888    assert_eq!(term.cursor_row(), 2);
889    assert_eq!(term.cursor_col(), 5);
890    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892    // Go left to the end of the overwritten text
893    term.cursor_left(1);
894    assert_eq!(term.cursor_row(), 2);
895    assert_eq!(term.cursor_col(), 4);
896    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898    // Scroll back down, removing the blank line at the top.
899    // Cursor stays in place, the text moves under it.
900    term.scroll(1);
901    assert_eq!(term.cursor_row(), 2);
902    assert_eq!(term.cursor_col(), 4);
903    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905    // Clear from here to end-of-line
906    term.clear_eol();
907    assert_eq!(term.cursor_row(), 2);
908    assert_eq!(term.cursor_col(), 4);
909    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911    // Now clear from here to start-of-line. Cursor does not move.
912    term.clear_sol();
913    assert_eq!(term.cursor_row(), 2);
914    assert_eq!(term.cursor_col(), 4);
915    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916    term.cursor_left(1);
917    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918    term.set_cursor_col(0);
919    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921    // Clear some lines
922    term.clear_line(1);
923    assert_eq!(term.cursor_row(), 2);
924    assert_eq!(term.cursor_col(), 0);
925    term.set_cursor_row(1);
926    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927    term.set_cursor_row(3);
928    term.clear_cur_line();
929    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930    assert_eq!(term.cursor_row(), 3);
931    assert_eq!(term.cursor_col(), 0);
932
933    term.append("Two lines above are intentionally left blank.\n");
934    assert_eq!(term.cursor_row(), 4);
935    assert_eq!(term.cursor_col(), 0);
936
937    // Set up the test pattern again, then play with insert/delete
938    term.append("0123456789\n");
939    term.append("ABCDEFGHIJ\n");
940    term.append("abcdefghij\n");
941    assert_eq!(term.cursor_row(), 7);
942
943    term.set_cursor_row(4);
944    term.set_cursor_col(4);
945    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947    term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949    term.cursor_right(5, false);
950    assert_eq!(term.cursor_col(), 9);
951    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953    // Insert two blank rows above cursor. Cursor stays put.
954    term.insert_rows(2);
955    assert_eq!(term.cursor_row(), 4);
956    assert_eq!(term.cursor_col(), 9);
957    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958    term.cursor_down(2, false); // Go down to find our text again
959    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961    // Go back to the beginning of the inserted 'x' characters and delete them.
962    term.cursor_left(5);
963    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964    term.delete_cur_chars(5);
965    assert_eq!(term.cursor_row(), 6);
966    assert_eq!(term.cursor_col(), 4);
967    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969    term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970    term.cursor_down(1, false);
971    term.cursor_left(2);
972    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974    term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975    assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976    term.cursor_up(1, false);
977    term.delete_rows(2); // Delete remains of test pattern
978
979    term.set_text_attrib(Attrib::Bold);
980    term.insert_char_eol('-', 3, 15, 20);
981    term.set_cursor_row(3);
982    term.set_cursor_col(15);
983    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984    assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986    term.set_text_attrib(Attrib::Italic);
987    term.append(" and all lines below");
988    term.set_text_attrib(Attrib::Normal);
989    term.cursor_down(1, false);
990
991    let mut hsb = term.hscrollbar();
992    let mut sb = term.scrollbar();
993    hsb.set_value(100.0);
994    assert_eq!(hsb.value(), 100.0);
995    sb.set_value(50.0);
996    assert_eq!(sb.value(), 50.0);
997    hsb.set_value(0.0);
998    assert_eq!(hsb.value(), 0.0);
999    sb.set_value(0.0);
1000    assert_eq!(sb.value(), 0.0);
1001}
Source

pub fn clear_sod(&mut self)

Clear from cursor to Start Of Display (EOD), like “<ESC>[1J”.

Source

pub fn clear_sol(&mut self)

Clear from cursor to Start Of Line (SOL), like “<ESC>[1K”.

Examples found in repository?
examples/terminal.rs (line 912)
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700    term.take_focus().unwrap();
701
702    // Test the attr_fg_color and attr_bg_color methods.
703    // Put a single character 'A' into the buffer and check it
704    term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705    term.set_text_bg_color(Color::TransparentBg);
706    term.set_text_fg_color(Color::XtermWhite);
707    term.append("A");
708    let r = &term.u8c_disp_row(0);
709    let uc = r.col(0);
710    assert_eq!(uc.text_utf8(), b"A");
711    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712    assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713    assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714    assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715    assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717    // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718    term.clear();
719    term.set_text_fg_color_xterm(XtermColor::White);
720    term.set_text_bg_color_xterm(XtermColor::Black);
721    assert_eq!(term.text_attrib(), Attrib::Normal);
722
723    assert!(term.ansi());
724    term.append("B\x1b[32mC\x1b[1mD\n");
725
726    let r = &term.u8c_disp_row(0);
727    let uc = r.col(0);
728    assert_eq!(uc.text_utf8(), b"B");
729    assert!(uc.is_char(b'B'));
730    assert!(!uc.is_char(b'A'));
731    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735    assert_eq!(
736        &uc.charflags(),
737        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738    );
739
740    let uc = r.col(1);
741    assert_eq!(uc.text_utf8(), b"C");
742    assert!(uc.is_char(b'C'));
743    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747    assert_eq!(
748        &uc.charflags(),
749        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750    );
751
752    let uc = r.col(2);
753    assert_eq!(uc.text_utf8(), b"D");
754    assert!(uc.is_char(b'D'));
755    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757    assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759    assert_eq!(
760        &uc.attr_fgcolor(Some(term)),
761        &Color::from_rgb(0x20, 0xf0, 0x20)
762    );
763    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764    assert_eq!(
765        &uc.charflags(),
766        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767    );
768
769    // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770    term.clear();
771    term.set_text_fg_color_xterm(XtermColor::White);
772    term.set_text_bg_color_xterm(XtermColor::Black);
773    term.set_text_attrib(Attrib::Normal);
774    assert_eq!(term.text_attrib(), Attrib::Normal);
775
776    assert!(term.ansi());
777    term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779    let r = &term.u8c_disp_row(0);
780    let uc = r.col(0);
781    assert_eq!(uc.text_utf8(), b"B");
782    assert!(uc.is_char(b'B'));
783    assert!(!uc.is_char(b'A'));
784    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788    assert_eq!(
789        &uc.charflags(),
790        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791    );
792
793    let uc = r.col(1);
794    assert_eq!(uc.text_utf8(), b"C");
795    assert!(uc.is_char(b'C'));
796    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800    assert_eq!(
801        &uc.charflags(),
802        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803    );
804
805    let uc = r.col(2);
806    assert_eq!(uc.text_utf8(), b"D");
807    assert!(uc.is_char(b'D'));
808    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812    assert_eq!(
813        &uc.charflags(),
814        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815    );
816
817    let uc = r.col(3);
818    assert_eq!(uc.text_utf8(), b"E");
819    assert!(uc.is_char(b'E'));
820    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822    assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823    assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824    assert_eq!(
825        &uc.charflags(),
826        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827    );
828
829    // Test some miscellaneous Utf8 constants
830    assert_eq!(uc.length(), 1);
831    assert_eq!(uc.max_utf8(), 4);
832    assert_eq!(uc.pwidth(), 9.0);
833    assert_eq!(uc.pwidth_int(), 9);
834
835    term.set_text_fg_color_xterm(XtermColor::White);
836    term.set_text_bg_color_xterm(XtermColor::Black);
837    term.clear();
838    term.set_text_attrib(Attrib::Normal);
839
840    // Mouse selection functions
841    term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842    term.clear_mouse_selection();
843    assert_eq!(term.get_selection(), None);
844
845    // Play with cursor position
846    term.append("0123456789\n"); // Set up test pattern
847    term.append("ABCDEFGHIJ\n");
848    term.append("abcdefghij\n");
849
850    term.set_cursor_row(1);
851    assert_eq!(term.cursor_row(), 1);
852    term.set_cursor_col(1);
853    assert_eq!(term.cursor_col(), 1);
854    assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856    term.append("----"); // Overwrites text at cursor and moves cursor forward
857    assert_eq!(term.cursor_row(), 1);
858    assert_eq!(term.cursor_col(), 5);
859    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860    term.set_cursor_col(1);
861    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863    term.cursor_up(1, false);
864    assert_eq!(term.cursor_row(), 0);
865    assert_eq!(term.cursor_col(), 1);
866    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868    // Hit top of screen, so nothing happens
869    term.cursor_up(1, false);
870    assert_eq!(term.cursor_row(), 0);
871    assert_eq!(term.cursor_col(), 1);
872    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874    // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875    term.cursor_up(1, true);
876    assert_eq!(term.cursor_row(), 0);
877    assert_eq!(term.cursor_col(), 1);
878    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880    // Go back down to the overwritten text
881    term.cursor_down(2, false);
882    assert_eq!(term.cursor_row(), 2);
883    assert_eq!(term.cursor_col(), 1);
884    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886    // Go right past the overwritten text
887    term.cursor_right(4, false);
888    assert_eq!(term.cursor_row(), 2);
889    assert_eq!(term.cursor_col(), 5);
890    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892    // Go left to the end of the overwritten text
893    term.cursor_left(1);
894    assert_eq!(term.cursor_row(), 2);
895    assert_eq!(term.cursor_col(), 4);
896    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898    // Scroll back down, removing the blank line at the top.
899    // Cursor stays in place, the text moves under it.
900    term.scroll(1);
901    assert_eq!(term.cursor_row(), 2);
902    assert_eq!(term.cursor_col(), 4);
903    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905    // Clear from here to end-of-line
906    term.clear_eol();
907    assert_eq!(term.cursor_row(), 2);
908    assert_eq!(term.cursor_col(), 4);
909    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911    // Now clear from here to start-of-line. Cursor does not move.
912    term.clear_sol();
913    assert_eq!(term.cursor_row(), 2);
914    assert_eq!(term.cursor_col(), 4);
915    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916    term.cursor_left(1);
917    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918    term.set_cursor_col(0);
919    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921    // Clear some lines
922    term.clear_line(1);
923    assert_eq!(term.cursor_row(), 2);
924    assert_eq!(term.cursor_col(), 0);
925    term.set_cursor_row(1);
926    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927    term.set_cursor_row(3);
928    term.clear_cur_line();
929    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930    assert_eq!(term.cursor_row(), 3);
931    assert_eq!(term.cursor_col(), 0);
932
933    term.append("Two lines above are intentionally left blank.\n");
934    assert_eq!(term.cursor_row(), 4);
935    assert_eq!(term.cursor_col(), 0);
936
937    // Set up the test pattern again, then play with insert/delete
938    term.append("0123456789\n");
939    term.append("ABCDEFGHIJ\n");
940    term.append("abcdefghij\n");
941    assert_eq!(term.cursor_row(), 7);
942
943    term.set_cursor_row(4);
944    term.set_cursor_col(4);
945    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947    term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949    term.cursor_right(5, false);
950    assert_eq!(term.cursor_col(), 9);
951    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953    // Insert two blank rows above cursor. Cursor stays put.
954    term.insert_rows(2);
955    assert_eq!(term.cursor_row(), 4);
956    assert_eq!(term.cursor_col(), 9);
957    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958    term.cursor_down(2, false); // Go down to find our text again
959    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961    // Go back to the beginning of the inserted 'x' characters and delete them.
962    term.cursor_left(5);
963    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964    term.delete_cur_chars(5);
965    assert_eq!(term.cursor_row(), 6);
966    assert_eq!(term.cursor_col(), 4);
967    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969    term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970    term.cursor_down(1, false);
971    term.cursor_left(2);
972    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974    term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975    assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976    term.cursor_up(1, false);
977    term.delete_rows(2); // Delete remains of test pattern
978
979    term.set_text_attrib(Attrib::Bold);
980    term.insert_char_eol('-', 3, 15, 20);
981    term.set_cursor_row(3);
982    term.set_cursor_col(15);
983    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984    assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986    term.set_text_attrib(Attrib::Italic);
987    term.append(" and all lines below");
988    term.set_text_attrib(Attrib::Normal);
989    term.cursor_down(1, false);
990
991    let mut hsb = term.hscrollbar();
992    let mut sb = term.scrollbar();
993    hsb.set_value(100.0);
994    assert_eq!(hsb.value(), 100.0);
995    sb.set_value(50.0);
996    assert_eq!(sb.value(), 50.0);
997    hsb.set_value(0.0);
998    assert_eq!(hsb.value(), 0.0);
999    sb.set_value(0.0);
1000    assert_eq!(sb.value(), 0.0);
1001}
Source

pub fn insert_char(&mut self, c: char, rep: i32)

Insert char c at the current cursor position for rep times. Works only for single-byte characters, c can’t be multi-byte UTF-8. Does not wrap; characters at end of line are lost. This is a low-level “protected” function of the fltk library

Examples found in repository?
examples/terminal.rs (line 947)
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700    term.take_focus().unwrap();
701
702    // Test the attr_fg_color and attr_bg_color methods.
703    // Put a single character 'A' into the buffer and check it
704    term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705    term.set_text_bg_color(Color::TransparentBg);
706    term.set_text_fg_color(Color::XtermWhite);
707    term.append("A");
708    let r = &term.u8c_disp_row(0);
709    let uc = r.col(0);
710    assert_eq!(uc.text_utf8(), b"A");
711    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712    assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713    assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714    assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715    assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717    // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718    term.clear();
719    term.set_text_fg_color_xterm(XtermColor::White);
720    term.set_text_bg_color_xterm(XtermColor::Black);
721    assert_eq!(term.text_attrib(), Attrib::Normal);
722
723    assert!(term.ansi());
724    term.append("B\x1b[32mC\x1b[1mD\n");
725
726    let r = &term.u8c_disp_row(0);
727    let uc = r.col(0);
728    assert_eq!(uc.text_utf8(), b"B");
729    assert!(uc.is_char(b'B'));
730    assert!(!uc.is_char(b'A'));
731    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735    assert_eq!(
736        &uc.charflags(),
737        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738    );
739
740    let uc = r.col(1);
741    assert_eq!(uc.text_utf8(), b"C");
742    assert!(uc.is_char(b'C'));
743    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747    assert_eq!(
748        &uc.charflags(),
749        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750    );
751
752    let uc = r.col(2);
753    assert_eq!(uc.text_utf8(), b"D");
754    assert!(uc.is_char(b'D'));
755    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757    assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759    assert_eq!(
760        &uc.attr_fgcolor(Some(term)),
761        &Color::from_rgb(0x20, 0xf0, 0x20)
762    );
763    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764    assert_eq!(
765        &uc.charflags(),
766        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767    );
768
769    // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770    term.clear();
771    term.set_text_fg_color_xterm(XtermColor::White);
772    term.set_text_bg_color_xterm(XtermColor::Black);
773    term.set_text_attrib(Attrib::Normal);
774    assert_eq!(term.text_attrib(), Attrib::Normal);
775
776    assert!(term.ansi());
777    term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779    let r = &term.u8c_disp_row(0);
780    let uc = r.col(0);
781    assert_eq!(uc.text_utf8(), b"B");
782    assert!(uc.is_char(b'B'));
783    assert!(!uc.is_char(b'A'));
784    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788    assert_eq!(
789        &uc.charflags(),
790        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791    );
792
793    let uc = r.col(1);
794    assert_eq!(uc.text_utf8(), b"C");
795    assert!(uc.is_char(b'C'));
796    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800    assert_eq!(
801        &uc.charflags(),
802        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803    );
804
805    let uc = r.col(2);
806    assert_eq!(uc.text_utf8(), b"D");
807    assert!(uc.is_char(b'D'));
808    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812    assert_eq!(
813        &uc.charflags(),
814        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815    );
816
817    let uc = r.col(3);
818    assert_eq!(uc.text_utf8(), b"E");
819    assert!(uc.is_char(b'E'));
820    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822    assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823    assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824    assert_eq!(
825        &uc.charflags(),
826        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827    );
828
829    // Test some miscellaneous Utf8 constants
830    assert_eq!(uc.length(), 1);
831    assert_eq!(uc.max_utf8(), 4);
832    assert_eq!(uc.pwidth(), 9.0);
833    assert_eq!(uc.pwidth_int(), 9);
834
835    term.set_text_fg_color_xterm(XtermColor::White);
836    term.set_text_bg_color_xterm(XtermColor::Black);
837    term.clear();
838    term.set_text_attrib(Attrib::Normal);
839
840    // Mouse selection functions
841    term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842    term.clear_mouse_selection();
843    assert_eq!(term.get_selection(), None);
844
845    // Play with cursor position
846    term.append("0123456789\n"); // Set up test pattern
847    term.append("ABCDEFGHIJ\n");
848    term.append("abcdefghij\n");
849
850    term.set_cursor_row(1);
851    assert_eq!(term.cursor_row(), 1);
852    term.set_cursor_col(1);
853    assert_eq!(term.cursor_col(), 1);
854    assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856    term.append("----"); // Overwrites text at cursor and moves cursor forward
857    assert_eq!(term.cursor_row(), 1);
858    assert_eq!(term.cursor_col(), 5);
859    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860    term.set_cursor_col(1);
861    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863    term.cursor_up(1, false);
864    assert_eq!(term.cursor_row(), 0);
865    assert_eq!(term.cursor_col(), 1);
866    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868    // Hit top of screen, so nothing happens
869    term.cursor_up(1, false);
870    assert_eq!(term.cursor_row(), 0);
871    assert_eq!(term.cursor_col(), 1);
872    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874    // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875    term.cursor_up(1, true);
876    assert_eq!(term.cursor_row(), 0);
877    assert_eq!(term.cursor_col(), 1);
878    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880    // Go back down to the overwritten text
881    term.cursor_down(2, false);
882    assert_eq!(term.cursor_row(), 2);
883    assert_eq!(term.cursor_col(), 1);
884    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886    // Go right past the overwritten text
887    term.cursor_right(4, false);
888    assert_eq!(term.cursor_row(), 2);
889    assert_eq!(term.cursor_col(), 5);
890    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892    // Go left to the end of the overwritten text
893    term.cursor_left(1);
894    assert_eq!(term.cursor_row(), 2);
895    assert_eq!(term.cursor_col(), 4);
896    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898    // Scroll back down, removing the blank line at the top.
899    // Cursor stays in place, the text moves under it.
900    term.scroll(1);
901    assert_eq!(term.cursor_row(), 2);
902    assert_eq!(term.cursor_col(), 4);
903    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905    // Clear from here to end-of-line
906    term.clear_eol();
907    assert_eq!(term.cursor_row(), 2);
908    assert_eq!(term.cursor_col(), 4);
909    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911    // Now clear from here to start-of-line. Cursor does not move.
912    term.clear_sol();
913    assert_eq!(term.cursor_row(), 2);
914    assert_eq!(term.cursor_col(), 4);
915    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916    term.cursor_left(1);
917    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918    term.set_cursor_col(0);
919    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921    // Clear some lines
922    term.clear_line(1);
923    assert_eq!(term.cursor_row(), 2);
924    assert_eq!(term.cursor_col(), 0);
925    term.set_cursor_row(1);
926    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927    term.set_cursor_row(3);
928    term.clear_cur_line();
929    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930    assert_eq!(term.cursor_row(), 3);
931    assert_eq!(term.cursor_col(), 0);
932
933    term.append("Two lines above are intentionally left blank.\n");
934    assert_eq!(term.cursor_row(), 4);
935    assert_eq!(term.cursor_col(), 0);
936
937    // Set up the test pattern again, then play with insert/delete
938    term.append("0123456789\n");
939    term.append("ABCDEFGHIJ\n");
940    term.append("abcdefghij\n");
941    assert_eq!(term.cursor_row(), 7);
942
943    term.set_cursor_row(4);
944    term.set_cursor_col(4);
945    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947    term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949    term.cursor_right(5, false);
950    assert_eq!(term.cursor_col(), 9);
951    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953    // Insert two blank rows above cursor. Cursor stays put.
954    term.insert_rows(2);
955    assert_eq!(term.cursor_row(), 4);
956    assert_eq!(term.cursor_col(), 9);
957    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958    term.cursor_down(2, false); // Go down to find our text again
959    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961    // Go back to the beginning of the inserted 'x' characters and delete them.
962    term.cursor_left(5);
963    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964    term.delete_cur_chars(5);
965    assert_eq!(term.cursor_row(), 6);
966    assert_eq!(term.cursor_col(), 4);
967    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969    term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970    term.cursor_down(1, false);
971    term.cursor_left(2);
972    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974    term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975    assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976    term.cursor_up(1, false);
977    term.delete_rows(2); // Delete remains of test pattern
978
979    term.set_text_attrib(Attrib::Bold);
980    term.insert_char_eol('-', 3, 15, 20);
981    term.set_cursor_row(3);
982    term.set_cursor_col(15);
983    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984    assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986    term.set_text_attrib(Attrib::Italic);
987    term.append(" and all lines below");
988    term.set_text_attrib(Attrib::Normal);
989    term.cursor_down(1, false);
990
991    let mut hsb = term.hscrollbar();
992    let mut sb = term.scrollbar();
993    hsb.set_value(100.0);
994    assert_eq!(hsb.value(), 100.0);
995    sb.set_value(50.0);
996    assert_eq!(sb.value(), 50.0);
997    hsb.set_value(0.0);
998    assert_eq!(hsb.value(), 0.0);
999    sb.set_value(0.0);
1000    assert_eq!(sb.value(), 0.0);
1001}
Source

pub fn insert_char_eol(&mut self, c: char, drow: i32, dcol: i32, rep: i32)

Insert char c for rep times at display row drow and column dcol. Works only for single-byte characters, c can’t be multi-byte UTF-8. Does not wrap; characters at end of line are lost. This is a low-level “protected” function of the fltk library

Examples found in repository?
examples/terminal.rs (line 980)
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700    term.take_focus().unwrap();
701
702    // Test the attr_fg_color and attr_bg_color methods.
703    // Put a single character 'A' into the buffer and check it
704    term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705    term.set_text_bg_color(Color::TransparentBg);
706    term.set_text_fg_color(Color::XtermWhite);
707    term.append("A");
708    let r = &term.u8c_disp_row(0);
709    let uc = r.col(0);
710    assert_eq!(uc.text_utf8(), b"A");
711    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712    assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713    assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714    assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715    assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717    // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718    term.clear();
719    term.set_text_fg_color_xterm(XtermColor::White);
720    term.set_text_bg_color_xterm(XtermColor::Black);
721    assert_eq!(term.text_attrib(), Attrib::Normal);
722
723    assert!(term.ansi());
724    term.append("B\x1b[32mC\x1b[1mD\n");
725
726    let r = &term.u8c_disp_row(0);
727    let uc = r.col(0);
728    assert_eq!(uc.text_utf8(), b"B");
729    assert!(uc.is_char(b'B'));
730    assert!(!uc.is_char(b'A'));
731    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735    assert_eq!(
736        &uc.charflags(),
737        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738    );
739
740    let uc = r.col(1);
741    assert_eq!(uc.text_utf8(), b"C");
742    assert!(uc.is_char(b'C'));
743    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747    assert_eq!(
748        &uc.charflags(),
749        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750    );
751
752    let uc = r.col(2);
753    assert_eq!(uc.text_utf8(), b"D");
754    assert!(uc.is_char(b'D'));
755    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757    assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759    assert_eq!(
760        &uc.attr_fgcolor(Some(term)),
761        &Color::from_rgb(0x20, 0xf0, 0x20)
762    );
763    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764    assert_eq!(
765        &uc.charflags(),
766        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767    );
768
769    // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770    term.clear();
771    term.set_text_fg_color_xterm(XtermColor::White);
772    term.set_text_bg_color_xterm(XtermColor::Black);
773    term.set_text_attrib(Attrib::Normal);
774    assert_eq!(term.text_attrib(), Attrib::Normal);
775
776    assert!(term.ansi());
777    term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779    let r = &term.u8c_disp_row(0);
780    let uc = r.col(0);
781    assert_eq!(uc.text_utf8(), b"B");
782    assert!(uc.is_char(b'B'));
783    assert!(!uc.is_char(b'A'));
784    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788    assert_eq!(
789        &uc.charflags(),
790        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791    );
792
793    let uc = r.col(1);
794    assert_eq!(uc.text_utf8(), b"C");
795    assert!(uc.is_char(b'C'));
796    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800    assert_eq!(
801        &uc.charflags(),
802        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803    );
804
805    let uc = r.col(2);
806    assert_eq!(uc.text_utf8(), b"D");
807    assert!(uc.is_char(b'D'));
808    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812    assert_eq!(
813        &uc.charflags(),
814        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815    );
816
817    let uc = r.col(3);
818    assert_eq!(uc.text_utf8(), b"E");
819    assert!(uc.is_char(b'E'));
820    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822    assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823    assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824    assert_eq!(
825        &uc.charflags(),
826        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827    );
828
829    // Test some miscellaneous Utf8 constants
830    assert_eq!(uc.length(), 1);
831    assert_eq!(uc.max_utf8(), 4);
832    assert_eq!(uc.pwidth(), 9.0);
833    assert_eq!(uc.pwidth_int(), 9);
834
835    term.set_text_fg_color_xterm(XtermColor::White);
836    term.set_text_bg_color_xterm(XtermColor::Black);
837    term.clear();
838    term.set_text_attrib(Attrib::Normal);
839
840    // Mouse selection functions
841    term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842    term.clear_mouse_selection();
843    assert_eq!(term.get_selection(), None);
844
845    // Play with cursor position
846    term.append("0123456789\n"); // Set up test pattern
847    term.append("ABCDEFGHIJ\n");
848    term.append("abcdefghij\n");
849
850    term.set_cursor_row(1);
851    assert_eq!(term.cursor_row(), 1);
852    term.set_cursor_col(1);
853    assert_eq!(term.cursor_col(), 1);
854    assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856    term.append("----"); // Overwrites text at cursor and moves cursor forward
857    assert_eq!(term.cursor_row(), 1);
858    assert_eq!(term.cursor_col(), 5);
859    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860    term.set_cursor_col(1);
861    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863    term.cursor_up(1, false);
864    assert_eq!(term.cursor_row(), 0);
865    assert_eq!(term.cursor_col(), 1);
866    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868    // Hit top of screen, so nothing happens
869    term.cursor_up(1, false);
870    assert_eq!(term.cursor_row(), 0);
871    assert_eq!(term.cursor_col(), 1);
872    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874    // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875    term.cursor_up(1, true);
876    assert_eq!(term.cursor_row(), 0);
877    assert_eq!(term.cursor_col(), 1);
878    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880    // Go back down to the overwritten text
881    term.cursor_down(2, false);
882    assert_eq!(term.cursor_row(), 2);
883    assert_eq!(term.cursor_col(), 1);
884    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886    // Go right past the overwritten text
887    term.cursor_right(4, false);
888    assert_eq!(term.cursor_row(), 2);
889    assert_eq!(term.cursor_col(), 5);
890    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892    // Go left to the end of the overwritten text
893    term.cursor_left(1);
894    assert_eq!(term.cursor_row(), 2);
895    assert_eq!(term.cursor_col(), 4);
896    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898    // Scroll back down, removing the blank line at the top.
899    // Cursor stays in place, the text moves under it.
900    term.scroll(1);
901    assert_eq!(term.cursor_row(), 2);
902    assert_eq!(term.cursor_col(), 4);
903    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905    // Clear from here to end-of-line
906    term.clear_eol();
907    assert_eq!(term.cursor_row(), 2);
908    assert_eq!(term.cursor_col(), 4);
909    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911    // Now clear from here to start-of-line. Cursor does not move.
912    term.clear_sol();
913    assert_eq!(term.cursor_row(), 2);
914    assert_eq!(term.cursor_col(), 4);
915    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916    term.cursor_left(1);
917    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918    term.set_cursor_col(0);
919    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921    // Clear some lines
922    term.clear_line(1);
923    assert_eq!(term.cursor_row(), 2);
924    assert_eq!(term.cursor_col(), 0);
925    term.set_cursor_row(1);
926    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927    term.set_cursor_row(3);
928    term.clear_cur_line();
929    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930    assert_eq!(term.cursor_row(), 3);
931    assert_eq!(term.cursor_col(), 0);
932
933    term.append("Two lines above are intentionally left blank.\n");
934    assert_eq!(term.cursor_row(), 4);
935    assert_eq!(term.cursor_col(), 0);
936
937    // Set up the test pattern again, then play with insert/delete
938    term.append("0123456789\n");
939    term.append("ABCDEFGHIJ\n");
940    term.append("abcdefghij\n");
941    assert_eq!(term.cursor_row(), 7);
942
943    term.set_cursor_row(4);
944    term.set_cursor_col(4);
945    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947    term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949    term.cursor_right(5, false);
950    assert_eq!(term.cursor_col(), 9);
951    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953    // Insert two blank rows above cursor. Cursor stays put.
954    term.insert_rows(2);
955    assert_eq!(term.cursor_row(), 4);
956    assert_eq!(term.cursor_col(), 9);
957    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958    term.cursor_down(2, false); // Go down to find our text again
959    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961    // Go back to the beginning of the inserted 'x' characters and delete them.
962    term.cursor_left(5);
963    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964    term.delete_cur_chars(5);
965    assert_eq!(term.cursor_row(), 6);
966    assert_eq!(term.cursor_col(), 4);
967    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969    term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970    term.cursor_down(1, false);
971    term.cursor_left(2);
972    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974    term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975    assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976    term.cursor_up(1, false);
977    term.delete_rows(2); // Delete remains of test pattern
978
979    term.set_text_attrib(Attrib::Bold);
980    term.insert_char_eol('-', 3, 15, 20);
981    term.set_cursor_row(3);
982    term.set_cursor_col(15);
983    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984    assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986    term.set_text_attrib(Attrib::Italic);
987    term.append(" and all lines below");
988    term.set_text_attrib(Attrib::Normal);
989    term.cursor_down(1, false);
990
991    let mut hsb = term.hscrollbar();
992    let mut sb = term.scrollbar();
993    hsb.set_value(100.0);
994    assert_eq!(hsb.value(), 100.0);
995    sb.set_value(50.0);
996    assert_eq!(sb.value(), 50.0);
997    hsb.set_value(0.0);
998    assert_eq!(hsb.value(), 0.0);
999    sb.set_value(0.0);
1000    assert_eq!(sb.value(), 0.0);
1001}
Source

pub fn insert_rows(&mut self, count: i32)

Insert count rows at current cursor position. Causes rows below to scroll down, and empty lines created. Lines deleted by scroll down are NOT moved into the scroll history. This is a low-level “protected” function of the fltk library

Examples found in repository?
examples/terminal.rs (line 954)
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700    term.take_focus().unwrap();
701
702    // Test the attr_fg_color and attr_bg_color methods.
703    // Put a single character 'A' into the buffer and check it
704    term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705    term.set_text_bg_color(Color::TransparentBg);
706    term.set_text_fg_color(Color::XtermWhite);
707    term.append("A");
708    let r = &term.u8c_disp_row(0);
709    let uc = r.col(0);
710    assert_eq!(uc.text_utf8(), b"A");
711    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712    assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713    assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714    assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715    assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717    // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718    term.clear();
719    term.set_text_fg_color_xterm(XtermColor::White);
720    term.set_text_bg_color_xterm(XtermColor::Black);
721    assert_eq!(term.text_attrib(), Attrib::Normal);
722
723    assert!(term.ansi());
724    term.append("B\x1b[32mC\x1b[1mD\n");
725
726    let r = &term.u8c_disp_row(0);
727    let uc = r.col(0);
728    assert_eq!(uc.text_utf8(), b"B");
729    assert!(uc.is_char(b'B'));
730    assert!(!uc.is_char(b'A'));
731    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735    assert_eq!(
736        &uc.charflags(),
737        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738    );
739
740    let uc = r.col(1);
741    assert_eq!(uc.text_utf8(), b"C");
742    assert!(uc.is_char(b'C'));
743    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747    assert_eq!(
748        &uc.charflags(),
749        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750    );
751
752    let uc = r.col(2);
753    assert_eq!(uc.text_utf8(), b"D");
754    assert!(uc.is_char(b'D'));
755    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757    assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759    assert_eq!(
760        &uc.attr_fgcolor(Some(term)),
761        &Color::from_rgb(0x20, 0xf0, 0x20)
762    );
763    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764    assert_eq!(
765        &uc.charflags(),
766        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767    );
768
769    // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770    term.clear();
771    term.set_text_fg_color_xterm(XtermColor::White);
772    term.set_text_bg_color_xterm(XtermColor::Black);
773    term.set_text_attrib(Attrib::Normal);
774    assert_eq!(term.text_attrib(), Attrib::Normal);
775
776    assert!(term.ansi());
777    term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779    let r = &term.u8c_disp_row(0);
780    let uc = r.col(0);
781    assert_eq!(uc.text_utf8(), b"B");
782    assert!(uc.is_char(b'B'));
783    assert!(!uc.is_char(b'A'));
784    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788    assert_eq!(
789        &uc.charflags(),
790        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791    );
792
793    let uc = r.col(1);
794    assert_eq!(uc.text_utf8(), b"C");
795    assert!(uc.is_char(b'C'));
796    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800    assert_eq!(
801        &uc.charflags(),
802        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803    );
804
805    let uc = r.col(2);
806    assert_eq!(uc.text_utf8(), b"D");
807    assert!(uc.is_char(b'D'));
808    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812    assert_eq!(
813        &uc.charflags(),
814        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815    );
816
817    let uc = r.col(3);
818    assert_eq!(uc.text_utf8(), b"E");
819    assert!(uc.is_char(b'E'));
820    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822    assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823    assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824    assert_eq!(
825        &uc.charflags(),
826        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827    );
828
829    // Test some miscellaneous Utf8 constants
830    assert_eq!(uc.length(), 1);
831    assert_eq!(uc.max_utf8(), 4);
832    assert_eq!(uc.pwidth(), 9.0);
833    assert_eq!(uc.pwidth_int(), 9);
834
835    term.set_text_fg_color_xterm(XtermColor::White);
836    term.set_text_bg_color_xterm(XtermColor::Black);
837    term.clear();
838    term.set_text_attrib(Attrib::Normal);
839
840    // Mouse selection functions
841    term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842    term.clear_mouse_selection();
843    assert_eq!(term.get_selection(), None);
844
845    // Play with cursor position
846    term.append("0123456789\n"); // Set up test pattern
847    term.append("ABCDEFGHIJ\n");
848    term.append("abcdefghij\n");
849
850    term.set_cursor_row(1);
851    assert_eq!(term.cursor_row(), 1);
852    term.set_cursor_col(1);
853    assert_eq!(term.cursor_col(), 1);
854    assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856    term.append("----"); // Overwrites text at cursor and moves cursor forward
857    assert_eq!(term.cursor_row(), 1);
858    assert_eq!(term.cursor_col(), 5);
859    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860    term.set_cursor_col(1);
861    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863    term.cursor_up(1, false);
864    assert_eq!(term.cursor_row(), 0);
865    assert_eq!(term.cursor_col(), 1);
866    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868    // Hit top of screen, so nothing happens
869    term.cursor_up(1, false);
870    assert_eq!(term.cursor_row(), 0);
871    assert_eq!(term.cursor_col(), 1);
872    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874    // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875    term.cursor_up(1, true);
876    assert_eq!(term.cursor_row(), 0);
877    assert_eq!(term.cursor_col(), 1);
878    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880    // Go back down to the overwritten text
881    term.cursor_down(2, false);
882    assert_eq!(term.cursor_row(), 2);
883    assert_eq!(term.cursor_col(), 1);
884    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886    // Go right past the overwritten text
887    term.cursor_right(4, false);
888    assert_eq!(term.cursor_row(), 2);
889    assert_eq!(term.cursor_col(), 5);
890    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892    // Go left to the end of the overwritten text
893    term.cursor_left(1);
894    assert_eq!(term.cursor_row(), 2);
895    assert_eq!(term.cursor_col(), 4);
896    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898    // Scroll back down, removing the blank line at the top.
899    // Cursor stays in place, the text moves under it.
900    term.scroll(1);
901    assert_eq!(term.cursor_row(), 2);
902    assert_eq!(term.cursor_col(), 4);
903    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905    // Clear from here to end-of-line
906    term.clear_eol();
907    assert_eq!(term.cursor_row(), 2);
908    assert_eq!(term.cursor_col(), 4);
909    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911    // Now clear from here to start-of-line. Cursor does not move.
912    term.clear_sol();
913    assert_eq!(term.cursor_row(), 2);
914    assert_eq!(term.cursor_col(), 4);
915    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916    term.cursor_left(1);
917    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918    term.set_cursor_col(0);
919    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921    // Clear some lines
922    term.clear_line(1);
923    assert_eq!(term.cursor_row(), 2);
924    assert_eq!(term.cursor_col(), 0);
925    term.set_cursor_row(1);
926    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927    term.set_cursor_row(3);
928    term.clear_cur_line();
929    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930    assert_eq!(term.cursor_row(), 3);
931    assert_eq!(term.cursor_col(), 0);
932
933    term.append("Two lines above are intentionally left blank.\n");
934    assert_eq!(term.cursor_row(), 4);
935    assert_eq!(term.cursor_col(), 0);
936
937    // Set up the test pattern again, then play with insert/delete
938    term.append("0123456789\n");
939    term.append("ABCDEFGHIJ\n");
940    term.append("abcdefghij\n");
941    assert_eq!(term.cursor_row(), 7);
942
943    term.set_cursor_row(4);
944    term.set_cursor_col(4);
945    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947    term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949    term.cursor_right(5, false);
950    assert_eq!(term.cursor_col(), 9);
951    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953    // Insert two blank rows above cursor. Cursor stays put.
954    term.insert_rows(2);
955    assert_eq!(term.cursor_row(), 4);
956    assert_eq!(term.cursor_col(), 9);
957    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958    term.cursor_down(2, false); // Go down to find our text again
959    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961    // Go back to the beginning of the inserted 'x' characters and delete them.
962    term.cursor_left(5);
963    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964    term.delete_cur_chars(5);
965    assert_eq!(term.cursor_row(), 6);
966    assert_eq!(term.cursor_col(), 4);
967    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969    term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970    term.cursor_down(1, false);
971    term.cursor_left(2);
972    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974    term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975    assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976    term.cursor_up(1, false);
977    term.delete_rows(2); // Delete remains of test pattern
978
979    term.set_text_attrib(Attrib::Bold);
980    term.insert_char_eol('-', 3, 15, 20);
981    term.set_cursor_row(3);
982    term.set_cursor_col(15);
983    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984    assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986    term.set_text_attrib(Attrib::Italic);
987    term.append(" and all lines below");
988    term.set_text_attrib(Attrib::Normal);
989    term.cursor_down(1, false);
990
991    let mut hsb = term.hscrollbar();
992    let mut sb = term.scrollbar();
993    hsb.set_value(100.0);
994    assert_eq!(hsb.value(), 100.0);
995    sb.set_value(50.0);
996    assert_eq!(sb.value(), 50.0);
997    hsb.set_value(0.0);
998    assert_eq!(hsb.value(), 0.0);
999    sb.set_value(0.0);
1000    assert_eq!(sb.value(), 0.0);
1001}
Source

pub fn delete_chars(&mut self, drow: i32, dcol: i32, count: i32)

Delete char(s) at (drow,dcol) for count times.

Examples found in repository?
examples/terminal.rs (line 969)
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700    term.take_focus().unwrap();
701
702    // Test the attr_fg_color and attr_bg_color methods.
703    // Put a single character 'A' into the buffer and check it
704    term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705    term.set_text_bg_color(Color::TransparentBg);
706    term.set_text_fg_color(Color::XtermWhite);
707    term.append("A");
708    let r = &term.u8c_disp_row(0);
709    let uc = r.col(0);
710    assert_eq!(uc.text_utf8(), b"A");
711    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712    assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713    assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714    assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715    assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717    // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718    term.clear();
719    term.set_text_fg_color_xterm(XtermColor::White);
720    term.set_text_bg_color_xterm(XtermColor::Black);
721    assert_eq!(term.text_attrib(), Attrib::Normal);
722
723    assert!(term.ansi());
724    term.append("B\x1b[32mC\x1b[1mD\n");
725
726    let r = &term.u8c_disp_row(0);
727    let uc = r.col(0);
728    assert_eq!(uc.text_utf8(), b"B");
729    assert!(uc.is_char(b'B'));
730    assert!(!uc.is_char(b'A'));
731    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735    assert_eq!(
736        &uc.charflags(),
737        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738    );
739
740    let uc = r.col(1);
741    assert_eq!(uc.text_utf8(), b"C");
742    assert!(uc.is_char(b'C'));
743    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747    assert_eq!(
748        &uc.charflags(),
749        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750    );
751
752    let uc = r.col(2);
753    assert_eq!(uc.text_utf8(), b"D");
754    assert!(uc.is_char(b'D'));
755    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757    assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759    assert_eq!(
760        &uc.attr_fgcolor(Some(term)),
761        &Color::from_rgb(0x20, 0xf0, 0x20)
762    );
763    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764    assert_eq!(
765        &uc.charflags(),
766        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767    );
768
769    // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770    term.clear();
771    term.set_text_fg_color_xterm(XtermColor::White);
772    term.set_text_bg_color_xterm(XtermColor::Black);
773    term.set_text_attrib(Attrib::Normal);
774    assert_eq!(term.text_attrib(), Attrib::Normal);
775
776    assert!(term.ansi());
777    term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779    let r = &term.u8c_disp_row(0);
780    let uc = r.col(0);
781    assert_eq!(uc.text_utf8(), b"B");
782    assert!(uc.is_char(b'B'));
783    assert!(!uc.is_char(b'A'));
784    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788    assert_eq!(
789        &uc.charflags(),
790        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791    );
792
793    let uc = r.col(1);
794    assert_eq!(uc.text_utf8(), b"C");
795    assert!(uc.is_char(b'C'));
796    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800    assert_eq!(
801        &uc.charflags(),
802        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803    );
804
805    let uc = r.col(2);
806    assert_eq!(uc.text_utf8(), b"D");
807    assert!(uc.is_char(b'D'));
808    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812    assert_eq!(
813        &uc.charflags(),
814        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815    );
816
817    let uc = r.col(3);
818    assert_eq!(uc.text_utf8(), b"E");
819    assert!(uc.is_char(b'E'));
820    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822    assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823    assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824    assert_eq!(
825        &uc.charflags(),
826        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827    );
828
829    // Test some miscellaneous Utf8 constants
830    assert_eq!(uc.length(), 1);
831    assert_eq!(uc.max_utf8(), 4);
832    assert_eq!(uc.pwidth(), 9.0);
833    assert_eq!(uc.pwidth_int(), 9);
834
835    term.set_text_fg_color_xterm(XtermColor::White);
836    term.set_text_bg_color_xterm(XtermColor::Black);
837    term.clear();
838    term.set_text_attrib(Attrib::Normal);
839
840    // Mouse selection functions
841    term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842    term.clear_mouse_selection();
843    assert_eq!(term.get_selection(), None);
844
845    // Play with cursor position
846    term.append("0123456789\n"); // Set up test pattern
847    term.append("ABCDEFGHIJ\n");
848    term.append("abcdefghij\n");
849
850    term.set_cursor_row(1);
851    assert_eq!(term.cursor_row(), 1);
852    term.set_cursor_col(1);
853    assert_eq!(term.cursor_col(), 1);
854    assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856    term.append("----"); // Overwrites text at cursor and moves cursor forward
857    assert_eq!(term.cursor_row(), 1);
858    assert_eq!(term.cursor_col(), 5);
859    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860    term.set_cursor_col(1);
861    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863    term.cursor_up(1, false);
864    assert_eq!(term.cursor_row(), 0);
865    assert_eq!(term.cursor_col(), 1);
866    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868    // Hit top of screen, so nothing happens
869    term.cursor_up(1, false);
870    assert_eq!(term.cursor_row(), 0);
871    assert_eq!(term.cursor_col(), 1);
872    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874    // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875    term.cursor_up(1, true);
876    assert_eq!(term.cursor_row(), 0);
877    assert_eq!(term.cursor_col(), 1);
878    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880    // Go back down to the overwritten text
881    term.cursor_down(2, false);
882    assert_eq!(term.cursor_row(), 2);
883    assert_eq!(term.cursor_col(), 1);
884    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886    // Go right past the overwritten text
887    term.cursor_right(4, false);
888    assert_eq!(term.cursor_row(), 2);
889    assert_eq!(term.cursor_col(), 5);
890    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892    // Go left to the end of the overwritten text
893    term.cursor_left(1);
894    assert_eq!(term.cursor_row(), 2);
895    assert_eq!(term.cursor_col(), 4);
896    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898    // Scroll back down, removing the blank line at the top.
899    // Cursor stays in place, the text moves under it.
900    term.scroll(1);
901    assert_eq!(term.cursor_row(), 2);
902    assert_eq!(term.cursor_col(), 4);
903    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905    // Clear from here to end-of-line
906    term.clear_eol();
907    assert_eq!(term.cursor_row(), 2);
908    assert_eq!(term.cursor_col(), 4);
909    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911    // Now clear from here to start-of-line. Cursor does not move.
912    term.clear_sol();
913    assert_eq!(term.cursor_row(), 2);
914    assert_eq!(term.cursor_col(), 4);
915    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916    term.cursor_left(1);
917    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918    term.set_cursor_col(0);
919    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921    // Clear some lines
922    term.clear_line(1);
923    assert_eq!(term.cursor_row(), 2);
924    assert_eq!(term.cursor_col(), 0);
925    term.set_cursor_row(1);
926    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927    term.set_cursor_row(3);
928    term.clear_cur_line();
929    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930    assert_eq!(term.cursor_row(), 3);
931    assert_eq!(term.cursor_col(), 0);
932
933    term.append("Two lines above are intentionally left blank.\n");
934    assert_eq!(term.cursor_row(), 4);
935    assert_eq!(term.cursor_col(), 0);
936
937    // Set up the test pattern again, then play with insert/delete
938    term.append("0123456789\n");
939    term.append("ABCDEFGHIJ\n");
940    term.append("abcdefghij\n");
941    assert_eq!(term.cursor_row(), 7);
942
943    term.set_cursor_row(4);
944    term.set_cursor_col(4);
945    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947    term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949    term.cursor_right(5, false);
950    assert_eq!(term.cursor_col(), 9);
951    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953    // Insert two blank rows above cursor. Cursor stays put.
954    term.insert_rows(2);
955    assert_eq!(term.cursor_row(), 4);
956    assert_eq!(term.cursor_col(), 9);
957    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958    term.cursor_down(2, false); // Go down to find our text again
959    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961    // Go back to the beginning of the inserted 'x' characters and delete them.
962    term.cursor_left(5);
963    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964    term.delete_cur_chars(5);
965    assert_eq!(term.cursor_row(), 6);
966    assert_eq!(term.cursor_col(), 4);
967    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969    term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970    term.cursor_down(1, false);
971    term.cursor_left(2);
972    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974    term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975    assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976    term.cursor_up(1, false);
977    term.delete_rows(2); // Delete remains of test pattern
978
979    term.set_text_attrib(Attrib::Bold);
980    term.insert_char_eol('-', 3, 15, 20);
981    term.set_cursor_row(3);
982    term.set_cursor_col(15);
983    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984    assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986    term.set_text_attrib(Attrib::Italic);
987    term.append(" and all lines below");
988    term.set_text_attrib(Attrib::Normal);
989    term.cursor_down(1, false);
990
991    let mut hsb = term.hscrollbar();
992    let mut sb = term.scrollbar();
993    hsb.set_value(100.0);
994    assert_eq!(hsb.value(), 100.0);
995    sb.set_value(50.0);
996    assert_eq!(sb.value(), 50.0);
997    hsb.set_value(0.0);
998    assert_eq!(hsb.value(), 0.0);
999    sb.set_value(0.0);
1000    assert_eq!(sb.value(), 0.0);
1001}
Source

pub fn delete_cur_chars(&mut self, count: i32)

Delete char(s) at cursor position for count times.

Examples found in repository?
examples/terminal.rs (line 964)
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700    term.take_focus().unwrap();
701
702    // Test the attr_fg_color and attr_bg_color methods.
703    // Put a single character 'A' into the buffer and check it
704    term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705    term.set_text_bg_color(Color::TransparentBg);
706    term.set_text_fg_color(Color::XtermWhite);
707    term.append("A");
708    let r = &term.u8c_disp_row(0);
709    let uc = r.col(0);
710    assert_eq!(uc.text_utf8(), b"A");
711    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712    assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713    assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714    assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715    assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717    // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718    term.clear();
719    term.set_text_fg_color_xterm(XtermColor::White);
720    term.set_text_bg_color_xterm(XtermColor::Black);
721    assert_eq!(term.text_attrib(), Attrib::Normal);
722
723    assert!(term.ansi());
724    term.append("B\x1b[32mC\x1b[1mD\n");
725
726    let r = &term.u8c_disp_row(0);
727    let uc = r.col(0);
728    assert_eq!(uc.text_utf8(), b"B");
729    assert!(uc.is_char(b'B'));
730    assert!(!uc.is_char(b'A'));
731    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735    assert_eq!(
736        &uc.charflags(),
737        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738    );
739
740    let uc = r.col(1);
741    assert_eq!(uc.text_utf8(), b"C");
742    assert!(uc.is_char(b'C'));
743    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747    assert_eq!(
748        &uc.charflags(),
749        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750    );
751
752    let uc = r.col(2);
753    assert_eq!(uc.text_utf8(), b"D");
754    assert!(uc.is_char(b'D'));
755    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757    assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759    assert_eq!(
760        &uc.attr_fgcolor(Some(term)),
761        &Color::from_rgb(0x20, 0xf0, 0x20)
762    );
763    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764    assert_eq!(
765        &uc.charflags(),
766        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767    );
768
769    // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770    term.clear();
771    term.set_text_fg_color_xterm(XtermColor::White);
772    term.set_text_bg_color_xterm(XtermColor::Black);
773    term.set_text_attrib(Attrib::Normal);
774    assert_eq!(term.text_attrib(), Attrib::Normal);
775
776    assert!(term.ansi());
777    term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779    let r = &term.u8c_disp_row(0);
780    let uc = r.col(0);
781    assert_eq!(uc.text_utf8(), b"B");
782    assert!(uc.is_char(b'B'));
783    assert!(!uc.is_char(b'A'));
784    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788    assert_eq!(
789        &uc.charflags(),
790        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791    );
792
793    let uc = r.col(1);
794    assert_eq!(uc.text_utf8(), b"C");
795    assert!(uc.is_char(b'C'));
796    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800    assert_eq!(
801        &uc.charflags(),
802        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803    );
804
805    let uc = r.col(2);
806    assert_eq!(uc.text_utf8(), b"D");
807    assert!(uc.is_char(b'D'));
808    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812    assert_eq!(
813        &uc.charflags(),
814        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815    );
816
817    let uc = r.col(3);
818    assert_eq!(uc.text_utf8(), b"E");
819    assert!(uc.is_char(b'E'));
820    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822    assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823    assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824    assert_eq!(
825        &uc.charflags(),
826        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827    );
828
829    // Test some miscellaneous Utf8 constants
830    assert_eq!(uc.length(), 1);
831    assert_eq!(uc.max_utf8(), 4);
832    assert_eq!(uc.pwidth(), 9.0);
833    assert_eq!(uc.pwidth_int(), 9);
834
835    term.set_text_fg_color_xterm(XtermColor::White);
836    term.set_text_bg_color_xterm(XtermColor::Black);
837    term.clear();
838    term.set_text_attrib(Attrib::Normal);
839
840    // Mouse selection functions
841    term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842    term.clear_mouse_selection();
843    assert_eq!(term.get_selection(), None);
844
845    // Play with cursor position
846    term.append("0123456789\n"); // Set up test pattern
847    term.append("ABCDEFGHIJ\n");
848    term.append("abcdefghij\n");
849
850    term.set_cursor_row(1);
851    assert_eq!(term.cursor_row(), 1);
852    term.set_cursor_col(1);
853    assert_eq!(term.cursor_col(), 1);
854    assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856    term.append("----"); // Overwrites text at cursor and moves cursor forward
857    assert_eq!(term.cursor_row(), 1);
858    assert_eq!(term.cursor_col(), 5);
859    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860    term.set_cursor_col(1);
861    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863    term.cursor_up(1, false);
864    assert_eq!(term.cursor_row(), 0);
865    assert_eq!(term.cursor_col(), 1);
866    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868    // Hit top of screen, so nothing happens
869    term.cursor_up(1, false);
870    assert_eq!(term.cursor_row(), 0);
871    assert_eq!(term.cursor_col(), 1);
872    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874    // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875    term.cursor_up(1, true);
876    assert_eq!(term.cursor_row(), 0);
877    assert_eq!(term.cursor_col(), 1);
878    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880    // Go back down to the overwritten text
881    term.cursor_down(2, false);
882    assert_eq!(term.cursor_row(), 2);
883    assert_eq!(term.cursor_col(), 1);
884    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886    // Go right past the overwritten text
887    term.cursor_right(4, false);
888    assert_eq!(term.cursor_row(), 2);
889    assert_eq!(term.cursor_col(), 5);
890    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892    // Go left to the end of the overwritten text
893    term.cursor_left(1);
894    assert_eq!(term.cursor_row(), 2);
895    assert_eq!(term.cursor_col(), 4);
896    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898    // Scroll back down, removing the blank line at the top.
899    // Cursor stays in place, the text moves under it.
900    term.scroll(1);
901    assert_eq!(term.cursor_row(), 2);
902    assert_eq!(term.cursor_col(), 4);
903    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905    // Clear from here to end-of-line
906    term.clear_eol();
907    assert_eq!(term.cursor_row(), 2);
908    assert_eq!(term.cursor_col(), 4);
909    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911    // Now clear from here to start-of-line. Cursor does not move.
912    term.clear_sol();
913    assert_eq!(term.cursor_row(), 2);
914    assert_eq!(term.cursor_col(), 4);
915    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916    term.cursor_left(1);
917    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918    term.set_cursor_col(0);
919    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921    // Clear some lines
922    term.clear_line(1);
923    assert_eq!(term.cursor_row(), 2);
924    assert_eq!(term.cursor_col(), 0);
925    term.set_cursor_row(1);
926    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927    term.set_cursor_row(3);
928    term.clear_cur_line();
929    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930    assert_eq!(term.cursor_row(), 3);
931    assert_eq!(term.cursor_col(), 0);
932
933    term.append("Two lines above are intentionally left blank.\n");
934    assert_eq!(term.cursor_row(), 4);
935    assert_eq!(term.cursor_col(), 0);
936
937    // Set up the test pattern again, then play with insert/delete
938    term.append("0123456789\n");
939    term.append("ABCDEFGHIJ\n");
940    term.append("abcdefghij\n");
941    assert_eq!(term.cursor_row(), 7);
942
943    term.set_cursor_row(4);
944    term.set_cursor_col(4);
945    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947    term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949    term.cursor_right(5, false);
950    assert_eq!(term.cursor_col(), 9);
951    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953    // Insert two blank rows above cursor. Cursor stays put.
954    term.insert_rows(2);
955    assert_eq!(term.cursor_row(), 4);
956    assert_eq!(term.cursor_col(), 9);
957    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958    term.cursor_down(2, false); // Go down to find our text again
959    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961    // Go back to the beginning of the inserted 'x' characters and delete them.
962    term.cursor_left(5);
963    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964    term.delete_cur_chars(5);
965    assert_eq!(term.cursor_row(), 6);
966    assert_eq!(term.cursor_col(), 4);
967    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969    term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970    term.cursor_down(1, false);
971    term.cursor_left(2);
972    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974    term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975    assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976    term.cursor_up(1, false);
977    term.delete_rows(2); // Delete remains of test pattern
978
979    term.set_text_attrib(Attrib::Bold);
980    term.insert_char_eol('-', 3, 15, 20);
981    term.set_cursor_row(3);
982    term.set_cursor_col(15);
983    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984    assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986    term.set_text_attrib(Attrib::Italic);
987    term.append(" and all lines below");
988    term.set_text_attrib(Attrib::Normal);
989    term.cursor_down(1, false);
990
991    let mut hsb = term.hscrollbar();
992    let mut sb = term.scrollbar();
993    hsb.set_value(100.0);
994    assert_eq!(hsb.value(), 100.0);
995    sb.set_value(50.0);
996    assert_eq!(sb.value(), 50.0);
997    hsb.set_value(0.0);
998    assert_eq!(hsb.value(), 0.0);
999    sb.set_value(0.0);
1000    assert_eq!(sb.value(), 0.0);
1001}
Source

pub fn delete_rows(&mut self, count: i32)

Delete count rows at cursor position. Causes rows to scroll up, and empty lines created at bottom of screen. Lines deleted by scroll up are NOT moved into the scroll history. This is a low-level “protected” function of the fltk library

Examples found in repository?
examples/terminal.rs (line 974)
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700    term.take_focus().unwrap();
701
702    // Test the attr_fg_color and attr_bg_color methods.
703    // Put a single character 'A' into the buffer and check it
704    term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705    term.set_text_bg_color(Color::TransparentBg);
706    term.set_text_fg_color(Color::XtermWhite);
707    term.append("A");
708    let r = &term.u8c_disp_row(0);
709    let uc = r.col(0);
710    assert_eq!(uc.text_utf8(), b"A");
711    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712    assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713    assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714    assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715    assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717    // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718    term.clear();
719    term.set_text_fg_color_xterm(XtermColor::White);
720    term.set_text_bg_color_xterm(XtermColor::Black);
721    assert_eq!(term.text_attrib(), Attrib::Normal);
722
723    assert!(term.ansi());
724    term.append("B\x1b[32mC\x1b[1mD\n");
725
726    let r = &term.u8c_disp_row(0);
727    let uc = r.col(0);
728    assert_eq!(uc.text_utf8(), b"B");
729    assert!(uc.is_char(b'B'));
730    assert!(!uc.is_char(b'A'));
731    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735    assert_eq!(
736        &uc.charflags(),
737        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738    );
739
740    let uc = r.col(1);
741    assert_eq!(uc.text_utf8(), b"C");
742    assert!(uc.is_char(b'C'));
743    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747    assert_eq!(
748        &uc.charflags(),
749        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750    );
751
752    let uc = r.col(2);
753    assert_eq!(uc.text_utf8(), b"D");
754    assert!(uc.is_char(b'D'));
755    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757    assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759    assert_eq!(
760        &uc.attr_fgcolor(Some(term)),
761        &Color::from_rgb(0x20, 0xf0, 0x20)
762    );
763    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764    assert_eq!(
765        &uc.charflags(),
766        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767    );
768
769    // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770    term.clear();
771    term.set_text_fg_color_xterm(XtermColor::White);
772    term.set_text_bg_color_xterm(XtermColor::Black);
773    term.set_text_attrib(Attrib::Normal);
774    assert_eq!(term.text_attrib(), Attrib::Normal);
775
776    assert!(term.ansi());
777    term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779    let r = &term.u8c_disp_row(0);
780    let uc = r.col(0);
781    assert_eq!(uc.text_utf8(), b"B");
782    assert!(uc.is_char(b'B'));
783    assert!(!uc.is_char(b'A'));
784    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788    assert_eq!(
789        &uc.charflags(),
790        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791    );
792
793    let uc = r.col(1);
794    assert_eq!(uc.text_utf8(), b"C");
795    assert!(uc.is_char(b'C'));
796    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800    assert_eq!(
801        &uc.charflags(),
802        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803    );
804
805    let uc = r.col(2);
806    assert_eq!(uc.text_utf8(), b"D");
807    assert!(uc.is_char(b'D'));
808    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812    assert_eq!(
813        &uc.charflags(),
814        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815    );
816
817    let uc = r.col(3);
818    assert_eq!(uc.text_utf8(), b"E");
819    assert!(uc.is_char(b'E'));
820    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822    assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823    assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824    assert_eq!(
825        &uc.charflags(),
826        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827    );
828
829    // Test some miscellaneous Utf8 constants
830    assert_eq!(uc.length(), 1);
831    assert_eq!(uc.max_utf8(), 4);
832    assert_eq!(uc.pwidth(), 9.0);
833    assert_eq!(uc.pwidth_int(), 9);
834
835    term.set_text_fg_color_xterm(XtermColor::White);
836    term.set_text_bg_color_xterm(XtermColor::Black);
837    term.clear();
838    term.set_text_attrib(Attrib::Normal);
839
840    // Mouse selection functions
841    term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842    term.clear_mouse_selection();
843    assert_eq!(term.get_selection(), None);
844
845    // Play with cursor position
846    term.append("0123456789\n"); // Set up test pattern
847    term.append("ABCDEFGHIJ\n");
848    term.append("abcdefghij\n");
849
850    term.set_cursor_row(1);
851    assert_eq!(term.cursor_row(), 1);
852    term.set_cursor_col(1);
853    assert_eq!(term.cursor_col(), 1);
854    assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856    term.append("----"); // Overwrites text at cursor and moves cursor forward
857    assert_eq!(term.cursor_row(), 1);
858    assert_eq!(term.cursor_col(), 5);
859    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860    term.set_cursor_col(1);
861    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863    term.cursor_up(1, false);
864    assert_eq!(term.cursor_row(), 0);
865    assert_eq!(term.cursor_col(), 1);
866    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868    // Hit top of screen, so nothing happens
869    term.cursor_up(1, false);
870    assert_eq!(term.cursor_row(), 0);
871    assert_eq!(term.cursor_col(), 1);
872    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874    // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875    term.cursor_up(1, true);
876    assert_eq!(term.cursor_row(), 0);
877    assert_eq!(term.cursor_col(), 1);
878    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880    // Go back down to the overwritten text
881    term.cursor_down(2, false);
882    assert_eq!(term.cursor_row(), 2);
883    assert_eq!(term.cursor_col(), 1);
884    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886    // Go right past the overwritten text
887    term.cursor_right(4, false);
888    assert_eq!(term.cursor_row(), 2);
889    assert_eq!(term.cursor_col(), 5);
890    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892    // Go left to the end of the overwritten text
893    term.cursor_left(1);
894    assert_eq!(term.cursor_row(), 2);
895    assert_eq!(term.cursor_col(), 4);
896    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898    // Scroll back down, removing the blank line at the top.
899    // Cursor stays in place, the text moves under it.
900    term.scroll(1);
901    assert_eq!(term.cursor_row(), 2);
902    assert_eq!(term.cursor_col(), 4);
903    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905    // Clear from here to end-of-line
906    term.clear_eol();
907    assert_eq!(term.cursor_row(), 2);
908    assert_eq!(term.cursor_col(), 4);
909    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911    // Now clear from here to start-of-line. Cursor does not move.
912    term.clear_sol();
913    assert_eq!(term.cursor_row(), 2);
914    assert_eq!(term.cursor_col(), 4);
915    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916    term.cursor_left(1);
917    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918    term.set_cursor_col(0);
919    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921    // Clear some lines
922    term.clear_line(1);
923    assert_eq!(term.cursor_row(), 2);
924    assert_eq!(term.cursor_col(), 0);
925    term.set_cursor_row(1);
926    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927    term.set_cursor_row(3);
928    term.clear_cur_line();
929    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930    assert_eq!(term.cursor_row(), 3);
931    assert_eq!(term.cursor_col(), 0);
932
933    term.append("Two lines above are intentionally left blank.\n");
934    assert_eq!(term.cursor_row(), 4);
935    assert_eq!(term.cursor_col(), 0);
936
937    // Set up the test pattern again, then play with insert/delete
938    term.append("0123456789\n");
939    term.append("ABCDEFGHIJ\n");
940    term.append("abcdefghij\n");
941    assert_eq!(term.cursor_row(), 7);
942
943    term.set_cursor_row(4);
944    term.set_cursor_col(4);
945    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947    term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949    term.cursor_right(5, false);
950    assert_eq!(term.cursor_col(), 9);
951    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953    // Insert two blank rows above cursor. Cursor stays put.
954    term.insert_rows(2);
955    assert_eq!(term.cursor_row(), 4);
956    assert_eq!(term.cursor_col(), 9);
957    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958    term.cursor_down(2, false); // Go down to find our text again
959    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961    // Go back to the beginning of the inserted 'x' characters and delete them.
962    term.cursor_left(5);
963    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964    term.delete_cur_chars(5);
965    assert_eq!(term.cursor_row(), 6);
966    assert_eq!(term.cursor_col(), 4);
967    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969    term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970    term.cursor_down(1, false);
971    term.cursor_left(2);
972    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974    term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975    assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976    term.cursor_up(1, false);
977    term.delete_rows(2); // Delete remains of test pattern
978
979    term.set_text_attrib(Attrib::Bold);
980    term.insert_char_eol('-', 3, 15, 20);
981    term.set_cursor_row(3);
982    term.set_cursor_col(15);
983    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984    assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986    term.set_text_attrib(Attrib::Italic);
987    term.append(" and all lines below");
988    term.set_text_attrib(Attrib::Normal);
989    term.cursor_down(1, false);
990
991    let mut hsb = term.hscrollbar();
992    let mut sb = term.scrollbar();
993    hsb.set_value(100.0);
994    assert_eq!(hsb.value(), 100.0);
995    sb.set_value(50.0);
996    assert_eq!(sb.value(), 50.0);
997    hsb.set_value(0.0);
998    assert_eq!(hsb.value(), 0.0);
999    sb.set_value(0.0);
1000    assert_eq!(sb.value(), 0.0);
1001}
Source

pub fn cursor_bg_color(&self) -> Color

Get the cursor’s background color used for the cursor itself.

Examples found in repository?
examples/terminal.rs (line 155)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn set_cursor_bg_color(&mut self, color: Color)

Set the cursor’s background color used for the cursor itself.

Examples found in repository?
examples/terminal.rs (line 159)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn cursor_fg_color(&self) -> Color

Get the cursor’s foreground color used for the cursor itself.

Examples found in repository?
examples/terminal.rs (line 163)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn set_cursor_fg_color(&mut self, color: Color)

Set the cursor’s foreground color used for the cursor itself.

Examples found in repository?
examples/terminal.rs (line 161)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn get_selection(&self) -> Option<[i32; 4]>

Get the current mouse selection. Returns None if no selection, or Some([srow, scol, erow, ecol]) if there is a selection, where row and col represent start/end positions in the ring buffer. This is a low-level “protected” function of the fltk library

Examples found in repository?
examples/terminal.rs (line 841)
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700    term.take_focus().unwrap();
701
702    // Test the attr_fg_color and attr_bg_color methods.
703    // Put a single character 'A' into the buffer and check it
704    term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705    term.set_text_bg_color(Color::TransparentBg);
706    term.set_text_fg_color(Color::XtermWhite);
707    term.append("A");
708    let r = &term.u8c_disp_row(0);
709    let uc = r.col(0);
710    assert_eq!(uc.text_utf8(), b"A");
711    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712    assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713    assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714    assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715    assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717    // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718    term.clear();
719    term.set_text_fg_color_xterm(XtermColor::White);
720    term.set_text_bg_color_xterm(XtermColor::Black);
721    assert_eq!(term.text_attrib(), Attrib::Normal);
722
723    assert!(term.ansi());
724    term.append("B\x1b[32mC\x1b[1mD\n");
725
726    let r = &term.u8c_disp_row(0);
727    let uc = r.col(0);
728    assert_eq!(uc.text_utf8(), b"B");
729    assert!(uc.is_char(b'B'));
730    assert!(!uc.is_char(b'A'));
731    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735    assert_eq!(
736        &uc.charflags(),
737        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738    );
739
740    let uc = r.col(1);
741    assert_eq!(uc.text_utf8(), b"C");
742    assert!(uc.is_char(b'C'));
743    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747    assert_eq!(
748        &uc.charflags(),
749        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750    );
751
752    let uc = r.col(2);
753    assert_eq!(uc.text_utf8(), b"D");
754    assert!(uc.is_char(b'D'));
755    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757    assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759    assert_eq!(
760        &uc.attr_fgcolor(Some(term)),
761        &Color::from_rgb(0x20, 0xf0, 0x20)
762    );
763    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764    assert_eq!(
765        &uc.charflags(),
766        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767    );
768
769    // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770    term.clear();
771    term.set_text_fg_color_xterm(XtermColor::White);
772    term.set_text_bg_color_xterm(XtermColor::Black);
773    term.set_text_attrib(Attrib::Normal);
774    assert_eq!(term.text_attrib(), Attrib::Normal);
775
776    assert!(term.ansi());
777    term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779    let r = &term.u8c_disp_row(0);
780    let uc = r.col(0);
781    assert_eq!(uc.text_utf8(), b"B");
782    assert!(uc.is_char(b'B'));
783    assert!(!uc.is_char(b'A'));
784    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788    assert_eq!(
789        &uc.charflags(),
790        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791    );
792
793    let uc = r.col(1);
794    assert_eq!(uc.text_utf8(), b"C");
795    assert!(uc.is_char(b'C'));
796    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800    assert_eq!(
801        &uc.charflags(),
802        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803    );
804
805    let uc = r.col(2);
806    assert_eq!(uc.text_utf8(), b"D");
807    assert!(uc.is_char(b'D'));
808    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812    assert_eq!(
813        &uc.charflags(),
814        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815    );
816
817    let uc = r.col(3);
818    assert_eq!(uc.text_utf8(), b"E");
819    assert!(uc.is_char(b'E'));
820    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822    assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823    assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824    assert_eq!(
825        &uc.charflags(),
826        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827    );
828
829    // Test some miscellaneous Utf8 constants
830    assert_eq!(uc.length(), 1);
831    assert_eq!(uc.max_utf8(), 4);
832    assert_eq!(uc.pwidth(), 9.0);
833    assert_eq!(uc.pwidth_int(), 9);
834
835    term.set_text_fg_color_xterm(XtermColor::White);
836    term.set_text_bg_color_xterm(XtermColor::Black);
837    term.clear();
838    term.set_text_attrib(Attrib::Normal);
839
840    // Mouse selection functions
841    term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842    term.clear_mouse_selection();
843    assert_eq!(term.get_selection(), None);
844
845    // Play with cursor position
846    term.append("0123456789\n"); // Set up test pattern
847    term.append("ABCDEFGHIJ\n");
848    term.append("abcdefghij\n");
849
850    term.set_cursor_row(1);
851    assert_eq!(term.cursor_row(), 1);
852    term.set_cursor_col(1);
853    assert_eq!(term.cursor_col(), 1);
854    assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856    term.append("----"); // Overwrites text at cursor and moves cursor forward
857    assert_eq!(term.cursor_row(), 1);
858    assert_eq!(term.cursor_col(), 5);
859    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860    term.set_cursor_col(1);
861    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863    term.cursor_up(1, false);
864    assert_eq!(term.cursor_row(), 0);
865    assert_eq!(term.cursor_col(), 1);
866    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868    // Hit top of screen, so nothing happens
869    term.cursor_up(1, false);
870    assert_eq!(term.cursor_row(), 0);
871    assert_eq!(term.cursor_col(), 1);
872    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874    // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875    term.cursor_up(1, true);
876    assert_eq!(term.cursor_row(), 0);
877    assert_eq!(term.cursor_col(), 1);
878    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880    // Go back down to the overwritten text
881    term.cursor_down(2, false);
882    assert_eq!(term.cursor_row(), 2);
883    assert_eq!(term.cursor_col(), 1);
884    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886    // Go right past the overwritten text
887    term.cursor_right(4, false);
888    assert_eq!(term.cursor_row(), 2);
889    assert_eq!(term.cursor_col(), 5);
890    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892    // Go left to the end of the overwritten text
893    term.cursor_left(1);
894    assert_eq!(term.cursor_row(), 2);
895    assert_eq!(term.cursor_col(), 4);
896    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898    // Scroll back down, removing the blank line at the top.
899    // Cursor stays in place, the text moves under it.
900    term.scroll(1);
901    assert_eq!(term.cursor_row(), 2);
902    assert_eq!(term.cursor_col(), 4);
903    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905    // Clear from here to end-of-line
906    term.clear_eol();
907    assert_eq!(term.cursor_row(), 2);
908    assert_eq!(term.cursor_col(), 4);
909    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911    // Now clear from here to start-of-line. Cursor does not move.
912    term.clear_sol();
913    assert_eq!(term.cursor_row(), 2);
914    assert_eq!(term.cursor_col(), 4);
915    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916    term.cursor_left(1);
917    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918    term.set_cursor_col(0);
919    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921    // Clear some lines
922    term.clear_line(1);
923    assert_eq!(term.cursor_row(), 2);
924    assert_eq!(term.cursor_col(), 0);
925    term.set_cursor_row(1);
926    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927    term.set_cursor_row(3);
928    term.clear_cur_line();
929    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930    assert_eq!(term.cursor_row(), 3);
931    assert_eq!(term.cursor_col(), 0);
932
933    term.append("Two lines above are intentionally left blank.\n");
934    assert_eq!(term.cursor_row(), 4);
935    assert_eq!(term.cursor_col(), 0);
936
937    // Set up the test pattern again, then play with insert/delete
938    term.append("0123456789\n");
939    term.append("ABCDEFGHIJ\n");
940    term.append("abcdefghij\n");
941    assert_eq!(term.cursor_row(), 7);
942
943    term.set_cursor_row(4);
944    term.set_cursor_col(4);
945    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947    term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949    term.cursor_right(5, false);
950    assert_eq!(term.cursor_col(), 9);
951    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953    // Insert two blank rows above cursor. Cursor stays put.
954    term.insert_rows(2);
955    assert_eq!(term.cursor_row(), 4);
956    assert_eq!(term.cursor_col(), 9);
957    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958    term.cursor_down(2, false); // Go down to find our text again
959    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961    // Go back to the beginning of the inserted 'x' characters and delete them.
962    term.cursor_left(5);
963    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964    term.delete_cur_chars(5);
965    assert_eq!(term.cursor_row(), 6);
966    assert_eq!(term.cursor_col(), 4);
967    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969    term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970    term.cursor_down(1, false);
971    term.cursor_left(2);
972    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974    term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975    assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976    term.cursor_up(1, false);
977    term.delete_rows(2); // Delete remains of test pattern
978
979    term.set_text_attrib(Attrib::Bold);
980    term.insert_char_eol('-', 3, 15, 20);
981    term.set_cursor_row(3);
982    term.set_cursor_col(15);
983    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984    assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986    term.set_text_attrib(Attrib::Italic);
987    term.append(" and all lines below");
988    term.set_text_attrib(Attrib::Normal);
989    term.cursor_down(1, false);
990
991    let mut hsb = term.hscrollbar();
992    let mut sb = term.scrollbar();
993    hsb.set_value(100.0);
994    assert_eq!(hsb.value(), 100.0);
995    sb.set_value(50.0);
996    assert_eq!(sb.value(), 50.0);
997    hsb.set_value(0.0);
998    assert_eq!(hsb.value(), 0.0);
999    sb.set_value(0.0);
1000    assert_eq!(sb.value(), 0.0);
1001}
Source

pub fn cursor_home(&mut self)

Move cursor to the home position (top/left).

Examples found in repository?
examples/terminal.rs (line 527)
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496    term.take_focus().unwrap();
497    term.reset_terminal();
498    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500    assert_eq!(term.history_use(), 0);
501    term.clear();
502    assert_eq!(term.cursor_row(), 0);
503    assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505    term.append("Test\ntext\na\nb\nc\nd");
506    assert_eq!(term.cursor_row(), 5);
507    let hist = term.history_use();
508    term.clear_screen_home(false);
509    assert_eq!(term.cursor_row(), 0);
510    assert_eq!(term.history_use(), hist); // History not changed
511
512    term.append("Test\ntext\na\nb\nc\nd\ne");
513    assert_eq!(term.cursor_row(), 6);
514    term.clear_screen_home(true);
515    assert_eq!(term.cursor_row(), 0);
516
517    term.append("Test\ntext\na\nb\nc\n");
518    assert_eq!(term.cursor_row(), 5);
519    term.clear_to_color(Color::DarkBlue);
520    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521    assert_eq!(term.text_bg_color(), Color::TransparentBg);
522    assert_eq!(term.cursor_row(), 0);
523
524    // Test cursor_home()
525    term.append("Test\n\n\n\n\n\n\n\n\n\n");
526    assert_eq!(term.cursor_row(), 10);
527    term.cursor_home();
528    assert_eq!(term.cursor_row(), 0);
529
530    // Test the widget color
531    assert_eq!(term.color(), Color::Black); // Default
532    term.set_color(Color::DarkGreen);
533    assert_eq!(term.color(), Color::DarkGreen);
534    term.set_color(Color::Black);
535    assert_eq!(term.color(), Color::Black);
536    term.append(
537        "This should be one line of white text on black, embedded into the top of a blue field.\n",
538    );
539
540    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541    term.set_output_translate(OutFlags::OFF);
542    assert_eq!(term.output_translate(), OutFlags::OFF);
543    term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547    term.append("\nDone!\n");
548    term.set_text_attrib(Attrib::Normal);
549}
Source

pub fn display_columns(&self) -> i32

Return terminal’s display width in columns of text characters.

Examples found in repository?
examples/terminal.rs (line 181)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
425//--------------------------------------------------------------------------------------
426/// More tests that run when the menu bar Test1 is clicked
427fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
428    term.take_focus().unwrap();
429    term.reset_terminal();
430    term.append("0123456789 0\n");
431    term.append("0123456789 1\n");
432    term.append("0123456789 2\n");
433    term.append("0123456789 3\n");
434    term.append("0123456789 4\n");
435    term.append("0123456789 5\n");
436    term.append("0123456789 6\n");
437    term.append("0123456789 7\n");
438    term.append("0123456789 8\n");
439    term.append("0123456789 9\n");
440    term.append("------------\n");
441
442    term.set_text_fg_color(Color::Green);
443    term.plot_char('A', 0, 0);
444    term.plot_char('B', 1, 1);
445    term.plot_char('C', 2, 2);
446    term.plot_char('D', 3, 3);
447    term.plot_char('E', 4, 4);
448    term.plot_char('F', 5, 5);
449    term.set_text_fg_color(Color::XtermWhite);
450
451    assert_eq!(term.cursor_row(), 11);
452    assert_eq!(term.cursor_col(), 0);
453
454    term.set_text_bg_color(Color::DarkBlue);
455    term.plot_char_utf8('b', 8, 1);
456    term.plot_char_utf8('↑', 9, 1);
457    term.plot_char_utf8('c', 8, 2);
458    term.plot_char_utf8('↑', 9, 2);
459    term.plot_char_utf8('d', 8, 3);
460    term.plot_char_utf8('↑', 9, 3);
461    term.plot_char_utf8('e', 8, 4);
462    term.plot_char_utf8('↑', 9, 4);
463    term.plot_char_utf8('f', 8, 5);
464    term.plot_char_utf8('↑', 9, 5);
465    term.plot_char_utf8('g', 8, 6);
466    term.plot_char_utf8('↑', 9, 6);
467    term.set_text_bg_color(Color::TransparentBg);
468
469    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
470    term.append("Done!\n");
471    term.set_text_attrib(Attrib::Normal);
472}
473
474//--------------------------------------------------------------------------------------
475/// More tests that run when the menu bar button Test2 is clicked
476fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
477    term.take_focus().unwrap();
478    term.reset_terminal();
479
480    for i in 0..50 {
481        term.append(&format!("{i}\n"));
482    }
483    assert_eq!(term.history_rows(), 100);
484
485    term.clear_history();
486    assert_eq!(term.history_use(), 0);
487
488    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
489    term.append("\nDone!\n");
490    term.set_text_attrib(Attrib::Normal);
491}
492
493//--------------------------------------------------------------------------------------
494/// Another set of tests that run when Test3 is clicked
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496    term.take_focus().unwrap();
497    term.reset_terminal();
498    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500    assert_eq!(term.history_use(), 0);
501    term.clear();
502    assert_eq!(term.cursor_row(), 0);
503    assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505    term.append("Test\ntext\na\nb\nc\nd");
506    assert_eq!(term.cursor_row(), 5);
507    let hist = term.history_use();
508    term.clear_screen_home(false);
509    assert_eq!(term.cursor_row(), 0);
510    assert_eq!(term.history_use(), hist); // History not changed
511
512    term.append("Test\ntext\na\nb\nc\nd\ne");
513    assert_eq!(term.cursor_row(), 6);
514    term.clear_screen_home(true);
515    assert_eq!(term.cursor_row(), 0);
516
517    term.append("Test\ntext\na\nb\nc\n");
518    assert_eq!(term.cursor_row(), 5);
519    term.clear_to_color(Color::DarkBlue);
520    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521    assert_eq!(term.text_bg_color(), Color::TransparentBg);
522    assert_eq!(term.cursor_row(), 0);
523
524    // Test cursor_home()
525    term.append("Test\n\n\n\n\n\n\n\n\n\n");
526    assert_eq!(term.cursor_row(), 10);
527    term.cursor_home();
528    assert_eq!(term.cursor_row(), 0);
529
530    // Test the widget color
531    assert_eq!(term.color(), Color::Black); // Default
532    term.set_color(Color::DarkGreen);
533    assert_eq!(term.color(), Color::DarkGreen);
534    term.set_color(Color::Black);
535    assert_eq!(term.color(), Color::Black);
536    term.append(
537        "This should be one line of white text on black, embedded into the top of a blue field.\n",
538    );
539
540    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541    term.set_output_translate(OutFlags::OFF);
542    assert_eq!(term.output_translate(), OutFlags::OFF);
543    term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547    term.append("\nDone!\n");
548    term.set_text_attrib(Attrib::Normal);
549}
550
551//--------------------------------------------------------------------------------------
552/// Another set of tests for the ring-buffer access methods
553/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555    let sel_len = term.selection_text_len();
556    let sel = term.selection_text();
557
558    term.take_focus().unwrap();
559    term.reset_terminal();
560    // Test the Utf8Char primitive
561    let uc = Utf8Char::new(b'Q');
562    let uc1 = uc.text_utf8();
563    assert_eq!(&uc1, b"Q");
564    assert_eq!(&uc.attrib(), &Attrib::Normal);
565    assert_eq!(
566        &uc.charflags(),
567        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568    );
569    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570    assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572    let ring_rows = term.ring_rows();
573
574    term.take_focus().unwrap();
575    term.clear_history();
576    assert_eq!(term.history_use(), 0);
577
578    // Subtract row numbers, modulo `rows`
579    fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580        match a - b {
581            n if n < 0 => n + rows,
582            n => n,
583        }
584    }
585    // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587    assert!(term.disp_srow() >= 0);
588    assert!(term.disp_erow() >= 0);
589    assert!(term.hist_srow() >= 0);
590    assert!(term.hist_erow() >= 0);
591    assert!(term.offset() >= 0);
592    assert!(term.disp_srow() <= ring_rows);
593    assert!(term.disp_erow() <= ring_rows);
594    assert!(term.hist_srow() <= ring_rows);
595    assert!(term.hist_erow() <= ring_rows);
596    assert!(term.offset() <= ring_rows);
597
598    assert_eq!(term.ring_srow(), 0);
599    assert_eq!(term.ring_erow(), ring_rows - 1);
600    assert_eq!(
601        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602        term.display_rows()
603    );
604    assert_eq!(
605        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606        term.history_rows()
607    );
608
609    assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610    assert_eq!(term.ring_srow(), 0);
611
612    /// Local function to read back all rows from the display into a long string.
613    /// Does not include scrollback history.
614    /// Trims trailing blanks on each line
615    fn read_disp(term: &Terminal) -> String {
616        let rows = term.display_rows();
617        let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618        for row in 0..rows {
619            let r = term.u8c_disp_row(row).trim();
620            // Iterate through a row, accumulating [u8]
621            for c in r.iter() {
622                // Note: Sometimes utf-8 length is > 1
623                text.extend_from_slice(c.text_utf8());
624            }
625            text.extend_from_slice(b"\n");
626        }
627        // Return the result as a string
628        std::str::from_utf8(&text).unwrap().to_string()
629    }
630
631    term.clear();
632    term.append("Top line  ↑ (up-arrow)");
633    term.set_text_attrib(Attrib::Underline);
634    term.append("  ");
635    term.set_text_attrib(Attrib::Normal);
636    term.append("  \n");
637    let mut text_out = read_disp(term);
638    // Trim trailing empty lines
639    text_out = text_out.trim_end_matches(&"\n").to_string();
640    // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642    assert_eq!(text_out, "Top line  ↑ (up-arrow)  ");
643    let r = term.u8c_disp_row(0);
644    assert_eq!(r.col(0).text_utf8(), b"T");
645    assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646    assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647    let r = term.u8c_disp_row(1);
648    assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649    assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651    // Clear the screen again, then append test text, then read it back and compare
652    let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656            Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659    term.clear_history();
660    term.clear();
661    let bg_save = term.text_bg_color();
662    let fg_save = term.text_fg_color();
663    term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664    term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665    term.append(test_text);
666    term.set_text_bg_color(bg_save);
667    term.set_text_fg_color(fg_save);
668
669    let mut text_out = read_disp(term);
670    // Trim trailing empty lines
671    text_out = text_out.trim_end_matches(&"\n").to_string();
672    assert_eq!(test_text, text_out);
673
674    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676    assert_eq!(term.ring_srow(), 0);
677    assert_eq!(term.ring_erow(), ring_rows - 1);
678    assert_eq!(
679        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680        term.display_rows()
681    );
682    assert_eq!(
683        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684        term.history_rows()
685    );
686
687    term.append(&format!(
688        "\n\nScreen has {} rows of {} columns.\n",
689        term.display_rows(),
690        term.display_columns()
691    ));
692
693    term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
Source

pub fn set_display_columns(&mut self, val: i32)

Set terminal’s display width in columns of text characters. Does not resize the terminal.

Examples found in repository?
examples/terminal.rs (line 183)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn display_rows(&self) -> i32

Return terminal’s display height in lines of text.

Examples found in repository?
examples/terminal.rs (line 170)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
425//--------------------------------------------------------------------------------------
426/// More tests that run when the menu bar Test1 is clicked
427fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
428    term.take_focus().unwrap();
429    term.reset_terminal();
430    term.append("0123456789 0\n");
431    term.append("0123456789 1\n");
432    term.append("0123456789 2\n");
433    term.append("0123456789 3\n");
434    term.append("0123456789 4\n");
435    term.append("0123456789 5\n");
436    term.append("0123456789 6\n");
437    term.append("0123456789 7\n");
438    term.append("0123456789 8\n");
439    term.append("0123456789 9\n");
440    term.append("------------\n");
441
442    term.set_text_fg_color(Color::Green);
443    term.plot_char('A', 0, 0);
444    term.plot_char('B', 1, 1);
445    term.plot_char('C', 2, 2);
446    term.plot_char('D', 3, 3);
447    term.plot_char('E', 4, 4);
448    term.plot_char('F', 5, 5);
449    term.set_text_fg_color(Color::XtermWhite);
450
451    assert_eq!(term.cursor_row(), 11);
452    assert_eq!(term.cursor_col(), 0);
453
454    term.set_text_bg_color(Color::DarkBlue);
455    term.plot_char_utf8('b', 8, 1);
456    term.plot_char_utf8('↑', 9, 1);
457    term.plot_char_utf8('c', 8, 2);
458    term.plot_char_utf8('↑', 9, 2);
459    term.plot_char_utf8('d', 8, 3);
460    term.plot_char_utf8('↑', 9, 3);
461    term.plot_char_utf8('e', 8, 4);
462    term.plot_char_utf8('↑', 9, 4);
463    term.plot_char_utf8('f', 8, 5);
464    term.plot_char_utf8('↑', 9, 5);
465    term.plot_char_utf8('g', 8, 6);
466    term.plot_char_utf8('↑', 9, 6);
467    term.set_text_bg_color(Color::TransparentBg);
468
469    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
470    term.append("Done!\n");
471    term.set_text_attrib(Attrib::Normal);
472}
473
474//--------------------------------------------------------------------------------------
475/// More tests that run when the menu bar button Test2 is clicked
476fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
477    term.take_focus().unwrap();
478    term.reset_terminal();
479
480    for i in 0..50 {
481        term.append(&format!("{i}\n"));
482    }
483    assert_eq!(term.history_rows(), 100);
484
485    term.clear_history();
486    assert_eq!(term.history_use(), 0);
487
488    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
489    term.append("\nDone!\n");
490    term.set_text_attrib(Attrib::Normal);
491}
492
493//--------------------------------------------------------------------------------------
494/// Another set of tests that run when Test3 is clicked
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496    term.take_focus().unwrap();
497    term.reset_terminal();
498    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500    assert_eq!(term.history_use(), 0);
501    term.clear();
502    assert_eq!(term.cursor_row(), 0);
503    assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505    term.append("Test\ntext\na\nb\nc\nd");
506    assert_eq!(term.cursor_row(), 5);
507    let hist = term.history_use();
508    term.clear_screen_home(false);
509    assert_eq!(term.cursor_row(), 0);
510    assert_eq!(term.history_use(), hist); // History not changed
511
512    term.append("Test\ntext\na\nb\nc\nd\ne");
513    assert_eq!(term.cursor_row(), 6);
514    term.clear_screen_home(true);
515    assert_eq!(term.cursor_row(), 0);
516
517    term.append("Test\ntext\na\nb\nc\n");
518    assert_eq!(term.cursor_row(), 5);
519    term.clear_to_color(Color::DarkBlue);
520    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521    assert_eq!(term.text_bg_color(), Color::TransparentBg);
522    assert_eq!(term.cursor_row(), 0);
523
524    // Test cursor_home()
525    term.append("Test\n\n\n\n\n\n\n\n\n\n");
526    assert_eq!(term.cursor_row(), 10);
527    term.cursor_home();
528    assert_eq!(term.cursor_row(), 0);
529
530    // Test the widget color
531    assert_eq!(term.color(), Color::Black); // Default
532    term.set_color(Color::DarkGreen);
533    assert_eq!(term.color(), Color::DarkGreen);
534    term.set_color(Color::Black);
535    assert_eq!(term.color(), Color::Black);
536    term.append(
537        "This should be one line of white text on black, embedded into the top of a blue field.\n",
538    );
539
540    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541    term.set_output_translate(OutFlags::OFF);
542    assert_eq!(term.output_translate(), OutFlags::OFF);
543    term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547    term.append("\nDone!\n");
548    term.set_text_attrib(Attrib::Normal);
549}
550
551//--------------------------------------------------------------------------------------
552/// Another set of tests for the ring-buffer access methods
553/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555    let sel_len = term.selection_text_len();
556    let sel = term.selection_text();
557
558    term.take_focus().unwrap();
559    term.reset_terminal();
560    // Test the Utf8Char primitive
561    let uc = Utf8Char::new(b'Q');
562    let uc1 = uc.text_utf8();
563    assert_eq!(&uc1, b"Q");
564    assert_eq!(&uc.attrib(), &Attrib::Normal);
565    assert_eq!(
566        &uc.charflags(),
567        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568    );
569    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570    assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572    let ring_rows = term.ring_rows();
573
574    term.take_focus().unwrap();
575    term.clear_history();
576    assert_eq!(term.history_use(), 0);
577
578    // Subtract row numbers, modulo `rows`
579    fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580        match a - b {
581            n if n < 0 => n + rows,
582            n => n,
583        }
584    }
585    // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587    assert!(term.disp_srow() >= 0);
588    assert!(term.disp_erow() >= 0);
589    assert!(term.hist_srow() >= 0);
590    assert!(term.hist_erow() >= 0);
591    assert!(term.offset() >= 0);
592    assert!(term.disp_srow() <= ring_rows);
593    assert!(term.disp_erow() <= ring_rows);
594    assert!(term.hist_srow() <= ring_rows);
595    assert!(term.hist_erow() <= ring_rows);
596    assert!(term.offset() <= ring_rows);
597
598    assert_eq!(term.ring_srow(), 0);
599    assert_eq!(term.ring_erow(), ring_rows - 1);
600    assert_eq!(
601        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602        term.display_rows()
603    );
604    assert_eq!(
605        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606        term.history_rows()
607    );
608
609    assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610    assert_eq!(term.ring_srow(), 0);
611
612    /// Local function to read back all rows from the display into a long string.
613    /// Does not include scrollback history.
614    /// Trims trailing blanks on each line
615    fn read_disp(term: &Terminal) -> String {
616        let rows = term.display_rows();
617        let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618        for row in 0..rows {
619            let r = term.u8c_disp_row(row).trim();
620            // Iterate through a row, accumulating [u8]
621            for c in r.iter() {
622                // Note: Sometimes utf-8 length is > 1
623                text.extend_from_slice(c.text_utf8());
624            }
625            text.extend_from_slice(b"\n");
626        }
627        // Return the result as a string
628        std::str::from_utf8(&text).unwrap().to_string()
629    }
630
631    term.clear();
632    term.append("Top line  ↑ (up-arrow)");
633    term.set_text_attrib(Attrib::Underline);
634    term.append("  ");
635    term.set_text_attrib(Attrib::Normal);
636    term.append("  \n");
637    let mut text_out = read_disp(term);
638    // Trim trailing empty lines
639    text_out = text_out.trim_end_matches(&"\n").to_string();
640    // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642    assert_eq!(text_out, "Top line  ↑ (up-arrow)  ");
643    let r = term.u8c_disp_row(0);
644    assert_eq!(r.col(0).text_utf8(), b"T");
645    assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646    assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647    let r = term.u8c_disp_row(1);
648    assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649    assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651    // Clear the screen again, then append test text, then read it back and compare
652    let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656            Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659    term.clear_history();
660    term.clear();
661    let bg_save = term.text_bg_color();
662    let fg_save = term.text_fg_color();
663    term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664    term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665    term.append(test_text);
666    term.set_text_bg_color(bg_save);
667    term.set_text_fg_color(fg_save);
668
669    let mut text_out = read_disp(term);
670    // Trim trailing empty lines
671    text_out = text_out.trim_end_matches(&"\n").to_string();
672    assert_eq!(test_text, text_out);
673
674    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676    assert_eq!(term.ring_srow(), 0);
677    assert_eq!(term.ring_erow(), ring_rows - 1);
678    assert_eq!(
679        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680        term.display_rows()
681    );
682    assert_eq!(
683        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684        term.history_rows()
685    );
686
687    term.append(&format!(
688        "\n\nScreen has {} rows of {} columns.\n",
689        term.display_rows(),
690        term.display_columns()
691    ));
692
693    term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
Source

pub fn set_history_rows(&mut self, arg1: i32)

Sets the terminal’s scrollback history buffer size in lines of text (rows).

Examples found in repository?
examples/terminal.rs (line 194)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn history_rows(&self) -> i32

Gets the terminal’s scrollback history buffer size in lines of text (rows).

Examples found in repository?
examples/terminal.rs (line 193)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
425//--------------------------------------------------------------------------------------
426/// More tests that run when the menu bar Test1 is clicked
427fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
428    term.take_focus().unwrap();
429    term.reset_terminal();
430    term.append("0123456789 0\n");
431    term.append("0123456789 1\n");
432    term.append("0123456789 2\n");
433    term.append("0123456789 3\n");
434    term.append("0123456789 4\n");
435    term.append("0123456789 5\n");
436    term.append("0123456789 6\n");
437    term.append("0123456789 7\n");
438    term.append("0123456789 8\n");
439    term.append("0123456789 9\n");
440    term.append("------------\n");
441
442    term.set_text_fg_color(Color::Green);
443    term.plot_char('A', 0, 0);
444    term.plot_char('B', 1, 1);
445    term.plot_char('C', 2, 2);
446    term.plot_char('D', 3, 3);
447    term.plot_char('E', 4, 4);
448    term.plot_char('F', 5, 5);
449    term.set_text_fg_color(Color::XtermWhite);
450
451    assert_eq!(term.cursor_row(), 11);
452    assert_eq!(term.cursor_col(), 0);
453
454    term.set_text_bg_color(Color::DarkBlue);
455    term.plot_char_utf8('b', 8, 1);
456    term.plot_char_utf8('↑', 9, 1);
457    term.plot_char_utf8('c', 8, 2);
458    term.plot_char_utf8('↑', 9, 2);
459    term.plot_char_utf8('d', 8, 3);
460    term.plot_char_utf8('↑', 9, 3);
461    term.plot_char_utf8('e', 8, 4);
462    term.plot_char_utf8('↑', 9, 4);
463    term.plot_char_utf8('f', 8, 5);
464    term.plot_char_utf8('↑', 9, 5);
465    term.plot_char_utf8('g', 8, 6);
466    term.plot_char_utf8('↑', 9, 6);
467    term.set_text_bg_color(Color::TransparentBg);
468
469    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
470    term.append("Done!\n");
471    term.set_text_attrib(Attrib::Normal);
472}
473
474//--------------------------------------------------------------------------------------
475/// More tests that run when the menu bar button Test2 is clicked
476fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
477    term.take_focus().unwrap();
478    term.reset_terminal();
479
480    for i in 0..50 {
481        term.append(&format!("{i}\n"));
482    }
483    assert_eq!(term.history_rows(), 100);
484
485    term.clear_history();
486    assert_eq!(term.history_use(), 0);
487
488    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
489    term.append("\nDone!\n");
490    term.set_text_attrib(Attrib::Normal);
491}
492
493//--------------------------------------------------------------------------------------
494/// Another set of tests that run when Test3 is clicked
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496    term.take_focus().unwrap();
497    term.reset_terminal();
498    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500    assert_eq!(term.history_use(), 0);
501    term.clear();
502    assert_eq!(term.cursor_row(), 0);
503    assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505    term.append("Test\ntext\na\nb\nc\nd");
506    assert_eq!(term.cursor_row(), 5);
507    let hist = term.history_use();
508    term.clear_screen_home(false);
509    assert_eq!(term.cursor_row(), 0);
510    assert_eq!(term.history_use(), hist); // History not changed
511
512    term.append("Test\ntext\na\nb\nc\nd\ne");
513    assert_eq!(term.cursor_row(), 6);
514    term.clear_screen_home(true);
515    assert_eq!(term.cursor_row(), 0);
516
517    term.append("Test\ntext\na\nb\nc\n");
518    assert_eq!(term.cursor_row(), 5);
519    term.clear_to_color(Color::DarkBlue);
520    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521    assert_eq!(term.text_bg_color(), Color::TransparentBg);
522    assert_eq!(term.cursor_row(), 0);
523
524    // Test cursor_home()
525    term.append("Test\n\n\n\n\n\n\n\n\n\n");
526    assert_eq!(term.cursor_row(), 10);
527    term.cursor_home();
528    assert_eq!(term.cursor_row(), 0);
529
530    // Test the widget color
531    assert_eq!(term.color(), Color::Black); // Default
532    term.set_color(Color::DarkGreen);
533    assert_eq!(term.color(), Color::DarkGreen);
534    term.set_color(Color::Black);
535    assert_eq!(term.color(), Color::Black);
536    term.append(
537        "This should be one line of white text on black, embedded into the top of a blue field.\n",
538    );
539
540    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541    term.set_output_translate(OutFlags::OFF);
542    assert_eq!(term.output_translate(), OutFlags::OFF);
543    term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547    term.append("\nDone!\n");
548    term.set_text_attrib(Attrib::Normal);
549}
550
551//--------------------------------------------------------------------------------------
552/// Another set of tests for the ring-buffer access methods
553/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555    let sel_len = term.selection_text_len();
556    let sel = term.selection_text();
557
558    term.take_focus().unwrap();
559    term.reset_terminal();
560    // Test the Utf8Char primitive
561    let uc = Utf8Char::new(b'Q');
562    let uc1 = uc.text_utf8();
563    assert_eq!(&uc1, b"Q");
564    assert_eq!(&uc.attrib(), &Attrib::Normal);
565    assert_eq!(
566        &uc.charflags(),
567        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568    );
569    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570    assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572    let ring_rows = term.ring_rows();
573
574    term.take_focus().unwrap();
575    term.clear_history();
576    assert_eq!(term.history_use(), 0);
577
578    // Subtract row numbers, modulo `rows`
579    fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580        match a - b {
581            n if n < 0 => n + rows,
582            n => n,
583        }
584    }
585    // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587    assert!(term.disp_srow() >= 0);
588    assert!(term.disp_erow() >= 0);
589    assert!(term.hist_srow() >= 0);
590    assert!(term.hist_erow() >= 0);
591    assert!(term.offset() >= 0);
592    assert!(term.disp_srow() <= ring_rows);
593    assert!(term.disp_erow() <= ring_rows);
594    assert!(term.hist_srow() <= ring_rows);
595    assert!(term.hist_erow() <= ring_rows);
596    assert!(term.offset() <= ring_rows);
597
598    assert_eq!(term.ring_srow(), 0);
599    assert_eq!(term.ring_erow(), ring_rows - 1);
600    assert_eq!(
601        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602        term.display_rows()
603    );
604    assert_eq!(
605        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606        term.history_rows()
607    );
608
609    assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610    assert_eq!(term.ring_srow(), 0);
611
612    /// Local function to read back all rows from the display into a long string.
613    /// Does not include scrollback history.
614    /// Trims trailing blanks on each line
615    fn read_disp(term: &Terminal) -> String {
616        let rows = term.display_rows();
617        let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618        for row in 0..rows {
619            let r = term.u8c_disp_row(row).trim();
620            // Iterate through a row, accumulating [u8]
621            for c in r.iter() {
622                // Note: Sometimes utf-8 length is > 1
623                text.extend_from_slice(c.text_utf8());
624            }
625            text.extend_from_slice(b"\n");
626        }
627        // Return the result as a string
628        std::str::from_utf8(&text).unwrap().to_string()
629    }
630
631    term.clear();
632    term.append("Top line  ↑ (up-arrow)");
633    term.set_text_attrib(Attrib::Underline);
634    term.append("  ");
635    term.set_text_attrib(Attrib::Normal);
636    term.append("  \n");
637    let mut text_out = read_disp(term);
638    // Trim trailing empty lines
639    text_out = text_out.trim_end_matches(&"\n").to_string();
640    // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642    assert_eq!(text_out, "Top line  ↑ (up-arrow)  ");
643    let r = term.u8c_disp_row(0);
644    assert_eq!(r.col(0).text_utf8(), b"T");
645    assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646    assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647    let r = term.u8c_disp_row(1);
648    assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649    assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651    // Clear the screen again, then append test text, then read it back and compare
652    let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656            Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659    term.clear_history();
660    term.clear();
661    let bg_save = term.text_bg_color();
662    let fg_save = term.text_fg_color();
663    term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664    term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665    term.append(test_text);
666    term.set_text_bg_color(bg_save);
667    term.set_text_fg_color(fg_save);
668
669    let mut text_out = read_disp(term);
670    // Trim trailing empty lines
671    text_out = text_out.trim_end_matches(&"\n").to_string();
672    assert_eq!(test_text, text_out);
673
674    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676    assert_eq!(term.ring_srow(), 0);
677    assert_eq!(term.ring_erow(), ring_rows - 1);
678    assert_eq!(
679        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680        term.display_rows()
681    );
682    assert_eq!(
683        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684        term.history_rows()
685    );
686
687    term.append(&format!(
688        "\n\nScreen has {} rows of {} columns.\n",
689        term.display_rows(),
690        term.display_columns()
691    ));
692
693    term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
Source

pub fn history_use(&self) -> i32

Returns how many lines are “in use” by the screen history buffer.

Examples found in repository?
examples/terminal.rs (line 199)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
425//--------------------------------------------------------------------------------------
426/// More tests that run when the menu bar Test1 is clicked
427fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
428    term.take_focus().unwrap();
429    term.reset_terminal();
430    term.append("0123456789 0\n");
431    term.append("0123456789 1\n");
432    term.append("0123456789 2\n");
433    term.append("0123456789 3\n");
434    term.append("0123456789 4\n");
435    term.append("0123456789 5\n");
436    term.append("0123456789 6\n");
437    term.append("0123456789 7\n");
438    term.append("0123456789 8\n");
439    term.append("0123456789 9\n");
440    term.append("------------\n");
441
442    term.set_text_fg_color(Color::Green);
443    term.plot_char('A', 0, 0);
444    term.plot_char('B', 1, 1);
445    term.plot_char('C', 2, 2);
446    term.plot_char('D', 3, 3);
447    term.plot_char('E', 4, 4);
448    term.plot_char('F', 5, 5);
449    term.set_text_fg_color(Color::XtermWhite);
450
451    assert_eq!(term.cursor_row(), 11);
452    assert_eq!(term.cursor_col(), 0);
453
454    term.set_text_bg_color(Color::DarkBlue);
455    term.plot_char_utf8('b', 8, 1);
456    term.plot_char_utf8('↑', 9, 1);
457    term.plot_char_utf8('c', 8, 2);
458    term.plot_char_utf8('↑', 9, 2);
459    term.plot_char_utf8('d', 8, 3);
460    term.plot_char_utf8('↑', 9, 3);
461    term.plot_char_utf8('e', 8, 4);
462    term.plot_char_utf8('↑', 9, 4);
463    term.plot_char_utf8('f', 8, 5);
464    term.plot_char_utf8('↑', 9, 5);
465    term.plot_char_utf8('g', 8, 6);
466    term.plot_char_utf8('↑', 9, 6);
467    term.set_text_bg_color(Color::TransparentBg);
468
469    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
470    term.append("Done!\n");
471    term.set_text_attrib(Attrib::Normal);
472}
473
474//--------------------------------------------------------------------------------------
475/// More tests that run when the menu bar button Test2 is clicked
476fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
477    term.take_focus().unwrap();
478    term.reset_terminal();
479
480    for i in 0..50 {
481        term.append(&format!("{i}\n"));
482    }
483    assert_eq!(term.history_rows(), 100);
484
485    term.clear_history();
486    assert_eq!(term.history_use(), 0);
487
488    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
489    term.append("\nDone!\n");
490    term.set_text_attrib(Attrib::Normal);
491}
492
493//--------------------------------------------------------------------------------------
494/// Another set of tests that run when Test3 is clicked
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496    term.take_focus().unwrap();
497    term.reset_terminal();
498    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500    assert_eq!(term.history_use(), 0);
501    term.clear();
502    assert_eq!(term.cursor_row(), 0);
503    assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505    term.append("Test\ntext\na\nb\nc\nd");
506    assert_eq!(term.cursor_row(), 5);
507    let hist = term.history_use();
508    term.clear_screen_home(false);
509    assert_eq!(term.cursor_row(), 0);
510    assert_eq!(term.history_use(), hist); // History not changed
511
512    term.append("Test\ntext\na\nb\nc\nd\ne");
513    assert_eq!(term.cursor_row(), 6);
514    term.clear_screen_home(true);
515    assert_eq!(term.cursor_row(), 0);
516
517    term.append("Test\ntext\na\nb\nc\n");
518    assert_eq!(term.cursor_row(), 5);
519    term.clear_to_color(Color::DarkBlue);
520    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521    assert_eq!(term.text_bg_color(), Color::TransparentBg);
522    assert_eq!(term.cursor_row(), 0);
523
524    // Test cursor_home()
525    term.append("Test\n\n\n\n\n\n\n\n\n\n");
526    assert_eq!(term.cursor_row(), 10);
527    term.cursor_home();
528    assert_eq!(term.cursor_row(), 0);
529
530    // Test the widget color
531    assert_eq!(term.color(), Color::Black); // Default
532    term.set_color(Color::DarkGreen);
533    assert_eq!(term.color(), Color::DarkGreen);
534    term.set_color(Color::Black);
535    assert_eq!(term.color(), Color::Black);
536    term.append(
537        "This should be one line of white text on black, embedded into the top of a blue field.\n",
538    );
539
540    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541    term.set_output_translate(OutFlags::OFF);
542    assert_eq!(term.output_translate(), OutFlags::OFF);
543    term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547    term.append("\nDone!\n");
548    term.set_text_attrib(Attrib::Normal);
549}
550
551//--------------------------------------------------------------------------------------
552/// Another set of tests for the ring-buffer access methods
553/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555    let sel_len = term.selection_text_len();
556    let sel = term.selection_text();
557
558    term.take_focus().unwrap();
559    term.reset_terminal();
560    // Test the Utf8Char primitive
561    let uc = Utf8Char::new(b'Q');
562    let uc1 = uc.text_utf8();
563    assert_eq!(&uc1, b"Q");
564    assert_eq!(&uc.attrib(), &Attrib::Normal);
565    assert_eq!(
566        &uc.charflags(),
567        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568    );
569    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570    assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572    let ring_rows = term.ring_rows();
573
574    term.take_focus().unwrap();
575    term.clear_history();
576    assert_eq!(term.history_use(), 0);
577
578    // Subtract row numbers, modulo `rows`
579    fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580        match a - b {
581            n if n < 0 => n + rows,
582            n => n,
583        }
584    }
585    // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587    assert!(term.disp_srow() >= 0);
588    assert!(term.disp_erow() >= 0);
589    assert!(term.hist_srow() >= 0);
590    assert!(term.hist_erow() >= 0);
591    assert!(term.offset() >= 0);
592    assert!(term.disp_srow() <= ring_rows);
593    assert!(term.disp_erow() <= ring_rows);
594    assert!(term.hist_srow() <= ring_rows);
595    assert!(term.hist_erow() <= ring_rows);
596    assert!(term.offset() <= ring_rows);
597
598    assert_eq!(term.ring_srow(), 0);
599    assert_eq!(term.ring_erow(), ring_rows - 1);
600    assert_eq!(
601        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602        term.display_rows()
603    );
604    assert_eq!(
605        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606        term.history_rows()
607    );
608
609    assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610    assert_eq!(term.ring_srow(), 0);
611
612    /// Local function to read back all rows from the display into a long string.
613    /// Does not include scrollback history.
614    /// Trims trailing blanks on each line
615    fn read_disp(term: &Terminal) -> String {
616        let rows = term.display_rows();
617        let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618        for row in 0..rows {
619            let r = term.u8c_disp_row(row).trim();
620            // Iterate through a row, accumulating [u8]
621            for c in r.iter() {
622                // Note: Sometimes utf-8 length is > 1
623                text.extend_from_slice(c.text_utf8());
624            }
625            text.extend_from_slice(b"\n");
626        }
627        // Return the result as a string
628        std::str::from_utf8(&text).unwrap().to_string()
629    }
630
631    term.clear();
632    term.append("Top line  ↑ (up-arrow)");
633    term.set_text_attrib(Attrib::Underline);
634    term.append("  ");
635    term.set_text_attrib(Attrib::Normal);
636    term.append("  \n");
637    let mut text_out = read_disp(term);
638    // Trim trailing empty lines
639    text_out = text_out.trim_end_matches(&"\n").to_string();
640    // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642    assert_eq!(text_out, "Top line  ↑ (up-arrow)  ");
643    let r = term.u8c_disp_row(0);
644    assert_eq!(r.col(0).text_utf8(), b"T");
645    assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646    assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647    let r = term.u8c_disp_row(1);
648    assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649    assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651    // Clear the screen again, then append test text, then read it back and compare
652    let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656            Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659    term.clear_history();
660    term.clear();
661    let bg_save = term.text_bg_color();
662    let fg_save = term.text_fg_color();
663    term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664    term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665    term.append(test_text);
666    term.set_text_bg_color(bg_save);
667    term.set_text_fg_color(fg_save);
668
669    let mut text_out = read_disp(term);
670    // Trim trailing empty lines
671    text_out = text_out.trim_end_matches(&"\n").to_string();
672    assert_eq!(test_text, text_out);
673
674    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676    assert_eq!(term.ring_srow(), 0);
677    assert_eq!(term.ring_erow(), ring_rows - 1);
678    assert_eq!(
679        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680        term.display_rows()
681    );
682    assert_eq!(
683        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684        term.history_rows()
685    );
686
687    term.append(&format!(
688        "\n\nScreen has {} rows of {} columns.\n",
689        term.display_rows(),
690        term.display_columns()
691    ));
692
693    term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
Source

pub fn set_margin_bottom(&mut self, arg1: i32)

Set the bottom margin

Examples found in repository?
examples/terminal.rs (line 217)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn margin_bottom(&self) -> i32

Return the bottom margin

Examples found in repository?
examples/terminal.rs (line 207)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn set_margin_left(&mut self, arg1: i32)

Set the left margin

Examples found in repository?
examples/terminal.rs (line 218)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn margin_left(&self) -> i32

Return the left margin

Examples found in repository?
examples/terminal.rs (line 208)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn set_margin_right(&mut self, arg1: i32)

Set the right margin

Examples found in repository?
examples/terminal.rs (line 219)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn margin_right(&self) -> i32

Return the right margin

Examples found in repository?
examples/terminal.rs (line 209)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn set_margin_top(&mut self, arg1: i32)

Set the top margin

Examples found in repository?
examples/terminal.rs (line 220)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn margin_top(&self) -> i32

Return the top margin

Examples found in repository?
examples/terminal.rs (line 210)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn h_to_row(&self, pix: i32) -> i32

Given a height in pixels, return number of rows that “fits” into that area. This is a low-level “protected” function of the fltk library

Examples found in repository?
examples/terminal.rs (line 349)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn w_to_col(&self, pix: i32) -> i32

Given a width in pixels, return number of columns that “fits” into that area. This is a low-level “protected” function of the fltk library

Examples found in repository?
examples/terminal.rs (line 350)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn set_output_translate(&mut self, val: OutFlags)

Sets the combined output translation flags to val.

val can be sensible combinations of the OutFlags bit flags.

The default is LF_TO_CRLF, so that \n will generate both carriage-return (CR) and line-feed (LF).

For \r and \n to be handled literally, use output_translate(Terminal::OutFlags::OFF); To disable all output translations, use 0 or Terminal::OutFlags::OFF.

Examples found in repository?
examples/terminal.rs (line 541)
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496    term.take_focus().unwrap();
497    term.reset_terminal();
498    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500    assert_eq!(term.history_use(), 0);
501    term.clear();
502    assert_eq!(term.cursor_row(), 0);
503    assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505    term.append("Test\ntext\na\nb\nc\nd");
506    assert_eq!(term.cursor_row(), 5);
507    let hist = term.history_use();
508    term.clear_screen_home(false);
509    assert_eq!(term.cursor_row(), 0);
510    assert_eq!(term.history_use(), hist); // History not changed
511
512    term.append("Test\ntext\na\nb\nc\nd\ne");
513    assert_eq!(term.cursor_row(), 6);
514    term.clear_screen_home(true);
515    assert_eq!(term.cursor_row(), 0);
516
517    term.append("Test\ntext\na\nb\nc\n");
518    assert_eq!(term.cursor_row(), 5);
519    term.clear_to_color(Color::DarkBlue);
520    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521    assert_eq!(term.text_bg_color(), Color::TransparentBg);
522    assert_eq!(term.cursor_row(), 0);
523
524    // Test cursor_home()
525    term.append("Test\n\n\n\n\n\n\n\n\n\n");
526    assert_eq!(term.cursor_row(), 10);
527    term.cursor_home();
528    assert_eq!(term.cursor_row(), 0);
529
530    // Test the widget color
531    assert_eq!(term.color(), Color::Black); // Default
532    term.set_color(Color::DarkGreen);
533    assert_eq!(term.color(), Color::DarkGreen);
534    term.set_color(Color::Black);
535    assert_eq!(term.color(), Color::Black);
536    term.append(
537        "This should be one line of white text on black, embedded into the top of a blue field.\n",
538    );
539
540    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541    term.set_output_translate(OutFlags::OFF);
542    assert_eq!(term.output_translate(), OutFlags::OFF);
543    term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547    term.append("\nDone!\n");
548    term.set_text_attrib(Attrib::Normal);
549}
Source

pub fn output_translate(&self) -> OutFlags

Return the current combined output translation flags.

Examples found in repository?
examples/terminal.rs (line 540)
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496    term.take_focus().unwrap();
497    term.reset_terminal();
498    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500    assert_eq!(term.history_use(), 0);
501    term.clear();
502    assert_eq!(term.cursor_row(), 0);
503    assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505    term.append("Test\ntext\na\nb\nc\nd");
506    assert_eq!(term.cursor_row(), 5);
507    let hist = term.history_use();
508    term.clear_screen_home(false);
509    assert_eq!(term.cursor_row(), 0);
510    assert_eq!(term.history_use(), hist); // History not changed
511
512    term.append("Test\ntext\na\nb\nc\nd\ne");
513    assert_eq!(term.cursor_row(), 6);
514    term.clear_screen_home(true);
515    assert_eq!(term.cursor_row(), 0);
516
517    term.append("Test\ntext\na\nb\nc\n");
518    assert_eq!(term.cursor_row(), 5);
519    term.clear_to_color(Color::DarkBlue);
520    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521    assert_eq!(term.text_bg_color(), Color::TransparentBg);
522    assert_eq!(term.cursor_row(), 0);
523
524    // Test cursor_home()
525    term.append("Test\n\n\n\n\n\n\n\n\n\n");
526    assert_eq!(term.cursor_row(), 10);
527    term.cursor_home();
528    assert_eq!(term.cursor_row(), 0);
529
530    // Test the widget color
531    assert_eq!(term.color(), Color::Black); // Default
532    term.set_color(Color::DarkGreen);
533    assert_eq!(term.color(), Color::DarkGreen);
534    term.set_color(Color::Black);
535    assert_eq!(term.color(), Color::Black);
536    term.append(
537        "This should be one line of white text on black, embedded into the top of a blue field.\n",
538    );
539
540    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541    term.set_output_translate(OutFlags::OFF);
542    assert_eq!(term.output_translate(), OutFlags::OFF);
543    term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547    term.append("\nDone!\n");
548    term.set_text_attrib(Attrib::Normal);
549}
Source

pub fn print_char(&mut self, c: char)

Prints single ASCII char c at current cursor position, and advances the cursor.

  • c must be ASCII, not utf-8
  • Does not trigger redraws
Examples found in repository?
examples/terminal.rs (line 227)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn print_char_utf8(&mut self, c: char)

Prints single UTF-8 char c at current cursor position, and advances the cursor if the character is printable. Handles ASCII and control codes (CR, LF, etc).

The character is displayed at the current cursor position using the current text color/attributes.

Handles control codes and can be used to construct ANSI/XTERM escape sequences.

  • c must be a single char only (whether UTF-8 or ASCII)
  • c can be an ASCII character, though not as efficent as print_char()
  • Invalid UTF-8 chars show the error character (¿) depending on show_unknown(bool).
  • Does not trigger redraws
Examples found in repository?
examples/terminal.rs (line 229)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn plot_char(&mut self, c: char, row: i32, col: i32)

Print the ASCII character c at the terminal’s display position (drow,dcol). The character MUST be printable (in range 0x20 - 0x7e), and is displayed using the current text color/attributes. Characters outside that range are either ignored or print the error character (¿), depending on show_unknown(bool).

No range checking is done on drow,dcol:

  • drow must be in range 0..(display_rows()-1)
  • dcol must be in range 0..(display_columns()-1)
  • Does not trigger redraws
  • Does NOT handle control codes, ANSI or XTERM escape sequences.
Examples found in repository?
examples/terminal.rs (line 443)
427fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
428    term.take_focus().unwrap();
429    term.reset_terminal();
430    term.append("0123456789 0\n");
431    term.append("0123456789 1\n");
432    term.append("0123456789 2\n");
433    term.append("0123456789 3\n");
434    term.append("0123456789 4\n");
435    term.append("0123456789 5\n");
436    term.append("0123456789 6\n");
437    term.append("0123456789 7\n");
438    term.append("0123456789 8\n");
439    term.append("0123456789 9\n");
440    term.append("------------\n");
441
442    term.set_text_fg_color(Color::Green);
443    term.plot_char('A', 0, 0);
444    term.plot_char('B', 1, 1);
445    term.plot_char('C', 2, 2);
446    term.plot_char('D', 3, 3);
447    term.plot_char('E', 4, 4);
448    term.plot_char('F', 5, 5);
449    term.set_text_fg_color(Color::XtermWhite);
450
451    assert_eq!(term.cursor_row(), 11);
452    assert_eq!(term.cursor_col(), 0);
453
454    term.set_text_bg_color(Color::DarkBlue);
455    term.plot_char_utf8('b', 8, 1);
456    term.plot_char_utf8('↑', 9, 1);
457    term.plot_char_utf8('c', 8, 2);
458    term.plot_char_utf8('↑', 9, 2);
459    term.plot_char_utf8('d', 8, 3);
460    term.plot_char_utf8('↑', 9, 3);
461    term.plot_char_utf8('e', 8, 4);
462    term.plot_char_utf8('↑', 9, 4);
463    term.plot_char_utf8('f', 8, 5);
464    term.plot_char_utf8('↑', 9, 5);
465    term.plot_char_utf8('g', 8, 6);
466    term.plot_char_utf8('↑', 9, 6);
467    term.set_text_bg_color(Color::TransparentBg);
468
469    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
470    term.append("Done!\n");
471    term.set_text_attrib(Attrib::Normal);
472}
Source

pub fn plot_char_utf8(&mut self, c: char, drow: i32, dcol: i32)

Print a single UTF-8 character len at display position (drow,dcol). The character is displayed using the current text color/attributes.

This is a very low level method. No range checking is done on drow,dcol:

  • drow must be in range 0..(display_rows()-1)
  • dcol must be in range 0..(display_columns()-1)
  • Does not trigger redraws
  • Does not handle ANSI or XTERM escape sequences
  • Invalid UTF-8 chars show the error character (¿) depending on show_unknown(bool).
Examples found in repository?
examples/terminal.rs (line 455)
427fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
428    term.take_focus().unwrap();
429    term.reset_terminal();
430    term.append("0123456789 0\n");
431    term.append("0123456789 1\n");
432    term.append("0123456789 2\n");
433    term.append("0123456789 3\n");
434    term.append("0123456789 4\n");
435    term.append("0123456789 5\n");
436    term.append("0123456789 6\n");
437    term.append("0123456789 7\n");
438    term.append("0123456789 8\n");
439    term.append("0123456789 9\n");
440    term.append("------------\n");
441
442    term.set_text_fg_color(Color::Green);
443    term.plot_char('A', 0, 0);
444    term.plot_char('B', 1, 1);
445    term.plot_char('C', 2, 2);
446    term.plot_char('D', 3, 3);
447    term.plot_char('E', 4, 4);
448    term.plot_char('F', 5, 5);
449    term.set_text_fg_color(Color::XtermWhite);
450
451    assert_eq!(term.cursor_row(), 11);
452    assert_eq!(term.cursor_col(), 0);
453
454    term.set_text_bg_color(Color::DarkBlue);
455    term.plot_char_utf8('b', 8, 1);
456    term.plot_char_utf8('↑', 9, 1);
457    term.plot_char_utf8('c', 8, 2);
458    term.plot_char_utf8('↑', 9, 2);
459    term.plot_char_utf8('d', 8, 3);
460    term.plot_char_utf8('↑', 9, 3);
461    term.plot_char_utf8('e', 8, 4);
462    term.plot_char_utf8('↑', 9, 4);
463    term.plot_char_utf8('f', 8, 5);
464    term.plot_char_utf8('↑', 9, 5);
465    term.plot_char_utf8('g', 8, 6);
466    term.plot_char_utf8('↑', 9, 6);
467    term.set_text_bg_color(Color::TransparentBg);
468
469    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
470    term.append("Done!\n");
471    term.set_text_attrib(Attrib::Normal);
472}
Source

pub fn set_redraw_rate(&mut self, set: f32)

Set the maximum rate redraw speed in floating point seconds if redraw_style() is set to RATE_LIMITED.

Examples found in repository?
examples/terminal.rs (line 235)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn redraw_rate(&self) -> f32

Get max rate redraw speed in floating point seconds.

Examples found in repository?
examples/terminal.rs (line 232)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn set_redraw_style(&mut self, set: RedrawStyle)

Set how Terminal manages screen redrawing.

Examples found in repository?
examples/terminal.rs (line 247)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn redraw_style(&self) -> RedrawStyle

Get the redraw style.

Examples found in repository?
examples/terminal.rs (line 240)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn reset_terminal(&mut self)

Resets terminal to default colors, clears screen, history and mouse selection, homes cursor, resets tabstops.

Examples found in repository?
examples/terminal.rs (line 429)
427fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
428    term.take_focus().unwrap();
429    term.reset_terminal();
430    term.append("0123456789 0\n");
431    term.append("0123456789 1\n");
432    term.append("0123456789 2\n");
433    term.append("0123456789 3\n");
434    term.append("0123456789 4\n");
435    term.append("0123456789 5\n");
436    term.append("0123456789 6\n");
437    term.append("0123456789 7\n");
438    term.append("0123456789 8\n");
439    term.append("0123456789 9\n");
440    term.append("------------\n");
441
442    term.set_text_fg_color(Color::Green);
443    term.plot_char('A', 0, 0);
444    term.plot_char('B', 1, 1);
445    term.plot_char('C', 2, 2);
446    term.plot_char('D', 3, 3);
447    term.plot_char('E', 4, 4);
448    term.plot_char('F', 5, 5);
449    term.set_text_fg_color(Color::XtermWhite);
450
451    assert_eq!(term.cursor_row(), 11);
452    assert_eq!(term.cursor_col(), 0);
453
454    term.set_text_bg_color(Color::DarkBlue);
455    term.plot_char_utf8('b', 8, 1);
456    term.plot_char_utf8('↑', 9, 1);
457    term.plot_char_utf8('c', 8, 2);
458    term.plot_char_utf8('↑', 9, 2);
459    term.plot_char_utf8('d', 8, 3);
460    term.plot_char_utf8('↑', 9, 3);
461    term.plot_char_utf8('e', 8, 4);
462    term.plot_char_utf8('↑', 9, 4);
463    term.plot_char_utf8('f', 8, 5);
464    term.plot_char_utf8('↑', 9, 5);
465    term.plot_char_utf8('g', 8, 6);
466    term.plot_char_utf8('↑', 9, 6);
467    term.set_text_bg_color(Color::TransparentBg);
468
469    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
470    term.append("Done!\n");
471    term.set_text_attrib(Attrib::Normal);
472}
473
474//--------------------------------------------------------------------------------------
475/// More tests that run when the menu bar button Test2 is clicked
476fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
477    term.take_focus().unwrap();
478    term.reset_terminal();
479
480    for i in 0..50 {
481        term.append(&format!("{i}\n"));
482    }
483    assert_eq!(term.history_rows(), 100);
484
485    term.clear_history();
486    assert_eq!(term.history_use(), 0);
487
488    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
489    term.append("\nDone!\n");
490    term.set_text_attrib(Attrib::Normal);
491}
492
493//--------------------------------------------------------------------------------------
494/// Another set of tests that run when Test3 is clicked
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496    term.take_focus().unwrap();
497    term.reset_terminal();
498    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500    assert_eq!(term.history_use(), 0);
501    term.clear();
502    assert_eq!(term.cursor_row(), 0);
503    assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505    term.append("Test\ntext\na\nb\nc\nd");
506    assert_eq!(term.cursor_row(), 5);
507    let hist = term.history_use();
508    term.clear_screen_home(false);
509    assert_eq!(term.cursor_row(), 0);
510    assert_eq!(term.history_use(), hist); // History not changed
511
512    term.append("Test\ntext\na\nb\nc\nd\ne");
513    assert_eq!(term.cursor_row(), 6);
514    term.clear_screen_home(true);
515    assert_eq!(term.cursor_row(), 0);
516
517    term.append("Test\ntext\na\nb\nc\n");
518    assert_eq!(term.cursor_row(), 5);
519    term.clear_to_color(Color::DarkBlue);
520    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521    assert_eq!(term.text_bg_color(), Color::TransparentBg);
522    assert_eq!(term.cursor_row(), 0);
523
524    // Test cursor_home()
525    term.append("Test\n\n\n\n\n\n\n\n\n\n");
526    assert_eq!(term.cursor_row(), 10);
527    term.cursor_home();
528    assert_eq!(term.cursor_row(), 0);
529
530    // Test the widget color
531    assert_eq!(term.color(), Color::Black); // Default
532    term.set_color(Color::DarkGreen);
533    assert_eq!(term.color(), Color::DarkGreen);
534    term.set_color(Color::Black);
535    assert_eq!(term.color(), Color::Black);
536    term.append(
537        "This should be one line of white text on black, embedded into the top of a blue field.\n",
538    );
539
540    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541    term.set_output_translate(OutFlags::OFF);
542    assert_eq!(term.output_translate(), OutFlags::OFF);
543    term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547    term.append("\nDone!\n");
548    term.set_text_attrib(Attrib::Normal);
549}
550
551//--------------------------------------------------------------------------------------
552/// Another set of tests for the ring-buffer access methods
553/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555    let sel_len = term.selection_text_len();
556    let sel = term.selection_text();
557
558    term.take_focus().unwrap();
559    term.reset_terminal();
560    // Test the Utf8Char primitive
561    let uc = Utf8Char::new(b'Q');
562    let uc1 = uc.text_utf8();
563    assert_eq!(&uc1, b"Q");
564    assert_eq!(&uc.attrib(), &Attrib::Normal);
565    assert_eq!(
566        &uc.charflags(),
567        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568    );
569    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570    assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572    let ring_rows = term.ring_rows();
573
574    term.take_focus().unwrap();
575    term.clear_history();
576    assert_eq!(term.history_use(), 0);
577
578    // Subtract row numbers, modulo `rows`
579    fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580        match a - b {
581            n if n < 0 => n + rows,
582            n => n,
583        }
584    }
585    // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587    assert!(term.disp_srow() >= 0);
588    assert!(term.disp_erow() >= 0);
589    assert!(term.hist_srow() >= 0);
590    assert!(term.hist_erow() >= 0);
591    assert!(term.offset() >= 0);
592    assert!(term.disp_srow() <= ring_rows);
593    assert!(term.disp_erow() <= ring_rows);
594    assert!(term.hist_srow() <= ring_rows);
595    assert!(term.hist_erow() <= ring_rows);
596    assert!(term.offset() <= ring_rows);
597
598    assert_eq!(term.ring_srow(), 0);
599    assert_eq!(term.ring_erow(), ring_rows - 1);
600    assert_eq!(
601        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602        term.display_rows()
603    );
604    assert_eq!(
605        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606        term.history_rows()
607    );
608
609    assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610    assert_eq!(term.ring_srow(), 0);
611
612    /// Local function to read back all rows from the display into a long string.
613    /// Does not include scrollback history.
614    /// Trims trailing blanks on each line
615    fn read_disp(term: &Terminal) -> String {
616        let rows = term.display_rows();
617        let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618        for row in 0..rows {
619            let r = term.u8c_disp_row(row).trim();
620            // Iterate through a row, accumulating [u8]
621            for c in r.iter() {
622                // Note: Sometimes utf-8 length is > 1
623                text.extend_from_slice(c.text_utf8());
624            }
625            text.extend_from_slice(b"\n");
626        }
627        // Return the result as a string
628        std::str::from_utf8(&text).unwrap().to_string()
629    }
630
631    term.clear();
632    term.append("Top line  ↑ (up-arrow)");
633    term.set_text_attrib(Attrib::Underline);
634    term.append("  ");
635    term.set_text_attrib(Attrib::Normal);
636    term.append("  \n");
637    let mut text_out = read_disp(term);
638    // Trim trailing empty lines
639    text_out = text_out.trim_end_matches(&"\n").to_string();
640    // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642    assert_eq!(text_out, "Top line  ↑ (up-arrow)  ");
643    let r = term.u8c_disp_row(0);
644    assert_eq!(r.col(0).text_utf8(), b"T");
645    assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646    assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647    let r = term.u8c_disp_row(1);
648    assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649    assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651    // Clear the screen again, then append test text, then read it back and compare
652    let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656            Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659    term.clear_history();
660    term.clear();
661    let bg_save = term.text_bg_color();
662    let fg_save = term.text_fg_color();
663    term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664    term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665    term.append(test_text);
666    term.set_text_bg_color(bg_save);
667    term.set_text_fg_color(fg_save);
668
669    let mut text_out = read_disp(term);
670    // Trim trailing empty lines
671    text_out = text_out.trim_end_matches(&"\n").to_string();
672    assert_eq!(test_text, text_out);
673
674    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676    assert_eq!(term.ring_srow(), 0);
677    assert_eq!(term.ring_erow(), ring_rows - 1);
678    assert_eq!(
679        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680        term.display_rows()
681    );
682    assert_eq!(
683        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684        term.history_rows()
685    );
686
687    term.append(&format!(
688        "\n\nScreen has {} rows of {} columns.\n",
689        term.display_rows(),
690        term.display_columns()
691    ));
692
693    term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
Source

pub fn scrollbar_actual_size(&self) -> i32

Returns the scrollbar’s actual “trough size”, which is the width of FL_VERTICAL scrollbars, or height of FL_HORIZONTAL scrollbars.

If scrollbar_size() is zero (default), then the value of the global Fl::scrollbar_size() is returned, which is the default global scrollbar size for the entire application.

Examples found in repository?
examples/terminal.rs (line 278)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn scrollbar_size(&self) -> i32

Get current pixel size of all the scrollbar’s troughs for this widget, or zero if the global Fl::scrollbar_size() is being used (default).

If this value returns zero, this widget’s scrollbars are using the global Fl::scrollbar_size(), in which case use scrollbar_actual_size() to get the actual (effective) pixel scrollbar size being used.

returns Scrollbar trough size in pixels, or 0 if the global Fl::scrollbar_size() is being used.

Examples found in repository?
examples/terminal.rs (line 281)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn set_scrollbar_size(&mut self, val: i32)

Set the width of the both horizontal and vertical scrollbar’s trough to val, in pixels. If this value is zero (default), this widget will use fltk’s master scrollbar_size() value

Examples found in repository?
examples/terminal.rs (line 287)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn selection_bg_color(&self) -> Color

Get mouse selection background color.

Examples found in repository?
examples/terminal.rs (line 299)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn scrollbar(&self) -> Scrollbar

Returns the vertical scrollbar

Examples found in repository?
examples/terminal.rs (line 269)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
425//--------------------------------------------------------------------------------------
426/// More tests that run when the menu bar Test1 is clicked
427fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
428    term.take_focus().unwrap();
429    term.reset_terminal();
430    term.append("0123456789 0\n");
431    term.append("0123456789 1\n");
432    term.append("0123456789 2\n");
433    term.append("0123456789 3\n");
434    term.append("0123456789 4\n");
435    term.append("0123456789 5\n");
436    term.append("0123456789 6\n");
437    term.append("0123456789 7\n");
438    term.append("0123456789 8\n");
439    term.append("0123456789 9\n");
440    term.append("------------\n");
441
442    term.set_text_fg_color(Color::Green);
443    term.plot_char('A', 0, 0);
444    term.plot_char('B', 1, 1);
445    term.plot_char('C', 2, 2);
446    term.plot_char('D', 3, 3);
447    term.plot_char('E', 4, 4);
448    term.plot_char('F', 5, 5);
449    term.set_text_fg_color(Color::XtermWhite);
450
451    assert_eq!(term.cursor_row(), 11);
452    assert_eq!(term.cursor_col(), 0);
453
454    term.set_text_bg_color(Color::DarkBlue);
455    term.plot_char_utf8('b', 8, 1);
456    term.plot_char_utf8('↑', 9, 1);
457    term.plot_char_utf8('c', 8, 2);
458    term.plot_char_utf8('↑', 9, 2);
459    term.plot_char_utf8('d', 8, 3);
460    term.plot_char_utf8('↑', 9, 3);
461    term.plot_char_utf8('e', 8, 4);
462    term.plot_char_utf8('↑', 9, 4);
463    term.plot_char_utf8('f', 8, 5);
464    term.plot_char_utf8('↑', 9, 5);
465    term.plot_char_utf8('g', 8, 6);
466    term.plot_char_utf8('↑', 9, 6);
467    term.set_text_bg_color(Color::TransparentBg);
468
469    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
470    term.append("Done!\n");
471    term.set_text_attrib(Attrib::Normal);
472}
473
474//--------------------------------------------------------------------------------------
475/// More tests that run when the menu bar button Test2 is clicked
476fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
477    term.take_focus().unwrap();
478    term.reset_terminal();
479
480    for i in 0..50 {
481        term.append(&format!("{i}\n"));
482    }
483    assert_eq!(term.history_rows(), 100);
484
485    term.clear_history();
486    assert_eq!(term.history_use(), 0);
487
488    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
489    term.append("\nDone!\n");
490    term.set_text_attrib(Attrib::Normal);
491}
492
493//--------------------------------------------------------------------------------------
494/// Another set of tests that run when Test3 is clicked
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496    term.take_focus().unwrap();
497    term.reset_terminal();
498    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500    assert_eq!(term.history_use(), 0);
501    term.clear();
502    assert_eq!(term.cursor_row(), 0);
503    assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505    term.append("Test\ntext\na\nb\nc\nd");
506    assert_eq!(term.cursor_row(), 5);
507    let hist = term.history_use();
508    term.clear_screen_home(false);
509    assert_eq!(term.cursor_row(), 0);
510    assert_eq!(term.history_use(), hist); // History not changed
511
512    term.append("Test\ntext\na\nb\nc\nd\ne");
513    assert_eq!(term.cursor_row(), 6);
514    term.clear_screen_home(true);
515    assert_eq!(term.cursor_row(), 0);
516
517    term.append("Test\ntext\na\nb\nc\n");
518    assert_eq!(term.cursor_row(), 5);
519    term.clear_to_color(Color::DarkBlue);
520    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521    assert_eq!(term.text_bg_color(), Color::TransparentBg);
522    assert_eq!(term.cursor_row(), 0);
523
524    // Test cursor_home()
525    term.append("Test\n\n\n\n\n\n\n\n\n\n");
526    assert_eq!(term.cursor_row(), 10);
527    term.cursor_home();
528    assert_eq!(term.cursor_row(), 0);
529
530    // Test the widget color
531    assert_eq!(term.color(), Color::Black); // Default
532    term.set_color(Color::DarkGreen);
533    assert_eq!(term.color(), Color::DarkGreen);
534    term.set_color(Color::Black);
535    assert_eq!(term.color(), Color::Black);
536    term.append(
537        "This should be one line of white text on black, embedded into the top of a blue field.\n",
538    );
539
540    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541    term.set_output_translate(OutFlags::OFF);
542    assert_eq!(term.output_translate(), OutFlags::OFF);
543    term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547    term.append("\nDone!\n");
548    term.set_text_attrib(Attrib::Normal);
549}
550
551//--------------------------------------------------------------------------------------
552/// Another set of tests for the ring-buffer access methods
553/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555    let sel_len = term.selection_text_len();
556    let sel = term.selection_text();
557
558    term.take_focus().unwrap();
559    term.reset_terminal();
560    // Test the Utf8Char primitive
561    let uc = Utf8Char::new(b'Q');
562    let uc1 = uc.text_utf8();
563    assert_eq!(&uc1, b"Q");
564    assert_eq!(&uc.attrib(), &Attrib::Normal);
565    assert_eq!(
566        &uc.charflags(),
567        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568    );
569    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570    assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572    let ring_rows = term.ring_rows();
573
574    term.take_focus().unwrap();
575    term.clear_history();
576    assert_eq!(term.history_use(), 0);
577
578    // Subtract row numbers, modulo `rows`
579    fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580        match a - b {
581            n if n < 0 => n + rows,
582            n => n,
583        }
584    }
585    // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587    assert!(term.disp_srow() >= 0);
588    assert!(term.disp_erow() >= 0);
589    assert!(term.hist_srow() >= 0);
590    assert!(term.hist_erow() >= 0);
591    assert!(term.offset() >= 0);
592    assert!(term.disp_srow() <= ring_rows);
593    assert!(term.disp_erow() <= ring_rows);
594    assert!(term.hist_srow() <= ring_rows);
595    assert!(term.hist_erow() <= ring_rows);
596    assert!(term.offset() <= ring_rows);
597
598    assert_eq!(term.ring_srow(), 0);
599    assert_eq!(term.ring_erow(), ring_rows - 1);
600    assert_eq!(
601        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602        term.display_rows()
603    );
604    assert_eq!(
605        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606        term.history_rows()
607    );
608
609    assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610    assert_eq!(term.ring_srow(), 0);
611
612    /// Local function to read back all rows from the display into a long string.
613    /// Does not include scrollback history.
614    /// Trims trailing blanks on each line
615    fn read_disp(term: &Terminal) -> String {
616        let rows = term.display_rows();
617        let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618        for row in 0..rows {
619            let r = term.u8c_disp_row(row).trim();
620            // Iterate through a row, accumulating [u8]
621            for c in r.iter() {
622                // Note: Sometimes utf-8 length is > 1
623                text.extend_from_slice(c.text_utf8());
624            }
625            text.extend_from_slice(b"\n");
626        }
627        // Return the result as a string
628        std::str::from_utf8(&text).unwrap().to_string()
629    }
630
631    term.clear();
632    term.append("Top line  ↑ (up-arrow)");
633    term.set_text_attrib(Attrib::Underline);
634    term.append("  ");
635    term.set_text_attrib(Attrib::Normal);
636    term.append("  \n");
637    let mut text_out = read_disp(term);
638    // Trim trailing empty lines
639    text_out = text_out.trim_end_matches(&"\n").to_string();
640    // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642    assert_eq!(text_out, "Top line  ↑ (up-arrow)  ");
643    let r = term.u8c_disp_row(0);
644    assert_eq!(r.col(0).text_utf8(), b"T");
645    assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646    assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647    let r = term.u8c_disp_row(1);
648    assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649    assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651    // Clear the screen again, then append test text, then read it back and compare
652    let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656            Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659    term.clear_history();
660    term.clear();
661    let bg_save = term.text_bg_color();
662    let fg_save = term.text_fg_color();
663    term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664    term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665    term.append(test_text);
666    term.set_text_bg_color(bg_save);
667    term.set_text_fg_color(fg_save);
668
669    let mut text_out = read_disp(term);
670    // Trim trailing empty lines
671    text_out = text_out.trim_end_matches(&"\n").to_string();
672    assert_eq!(test_text, text_out);
673
674    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676    assert_eq!(term.ring_srow(), 0);
677    assert_eq!(term.ring_erow(), ring_rows - 1);
678    assert_eq!(
679        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680        term.display_rows()
681    );
682    assert_eq!(
683        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684        term.history_rows()
685    );
686
687    term.append(&format!(
688        "\n\nScreen has {} rows of {} columns.\n",
689        term.display_rows(),
690        term.display_columns()
691    ));
692
693    term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
695
696//--------------------------------------------------------------------------------------
697/// Yet another set of tests for misc cursor functions and other stuff
698/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700    term.take_focus().unwrap();
701
702    // Test the attr_fg_color and attr_bg_color methods.
703    // Put a single character 'A' into the buffer and check it
704    term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705    term.set_text_bg_color(Color::TransparentBg);
706    term.set_text_fg_color(Color::XtermWhite);
707    term.append("A");
708    let r = &term.u8c_disp_row(0);
709    let uc = r.col(0);
710    assert_eq!(uc.text_utf8(), b"A");
711    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712    assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713    assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714    assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715    assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717    // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718    term.clear();
719    term.set_text_fg_color_xterm(XtermColor::White);
720    term.set_text_bg_color_xterm(XtermColor::Black);
721    assert_eq!(term.text_attrib(), Attrib::Normal);
722
723    assert!(term.ansi());
724    term.append("B\x1b[32mC\x1b[1mD\n");
725
726    let r = &term.u8c_disp_row(0);
727    let uc = r.col(0);
728    assert_eq!(uc.text_utf8(), b"B");
729    assert!(uc.is_char(b'B'));
730    assert!(!uc.is_char(b'A'));
731    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735    assert_eq!(
736        &uc.charflags(),
737        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738    );
739
740    let uc = r.col(1);
741    assert_eq!(uc.text_utf8(), b"C");
742    assert!(uc.is_char(b'C'));
743    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747    assert_eq!(
748        &uc.charflags(),
749        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750    );
751
752    let uc = r.col(2);
753    assert_eq!(uc.text_utf8(), b"D");
754    assert!(uc.is_char(b'D'));
755    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757    assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759    assert_eq!(
760        &uc.attr_fgcolor(Some(term)),
761        &Color::from_rgb(0x20, 0xf0, 0x20)
762    );
763    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764    assert_eq!(
765        &uc.charflags(),
766        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767    );
768
769    // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770    term.clear();
771    term.set_text_fg_color_xterm(XtermColor::White);
772    term.set_text_bg_color_xterm(XtermColor::Black);
773    term.set_text_attrib(Attrib::Normal);
774    assert_eq!(term.text_attrib(), Attrib::Normal);
775
776    assert!(term.ansi());
777    term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779    let r = &term.u8c_disp_row(0);
780    let uc = r.col(0);
781    assert_eq!(uc.text_utf8(), b"B");
782    assert!(uc.is_char(b'B'));
783    assert!(!uc.is_char(b'A'));
784    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788    assert_eq!(
789        &uc.charflags(),
790        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791    );
792
793    let uc = r.col(1);
794    assert_eq!(uc.text_utf8(), b"C");
795    assert!(uc.is_char(b'C'));
796    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800    assert_eq!(
801        &uc.charflags(),
802        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803    );
804
805    let uc = r.col(2);
806    assert_eq!(uc.text_utf8(), b"D");
807    assert!(uc.is_char(b'D'));
808    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812    assert_eq!(
813        &uc.charflags(),
814        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815    );
816
817    let uc = r.col(3);
818    assert_eq!(uc.text_utf8(), b"E");
819    assert!(uc.is_char(b'E'));
820    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822    assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823    assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824    assert_eq!(
825        &uc.charflags(),
826        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827    );
828
829    // Test some miscellaneous Utf8 constants
830    assert_eq!(uc.length(), 1);
831    assert_eq!(uc.max_utf8(), 4);
832    assert_eq!(uc.pwidth(), 9.0);
833    assert_eq!(uc.pwidth_int(), 9);
834
835    term.set_text_fg_color_xterm(XtermColor::White);
836    term.set_text_bg_color_xterm(XtermColor::Black);
837    term.clear();
838    term.set_text_attrib(Attrib::Normal);
839
840    // Mouse selection functions
841    term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842    term.clear_mouse_selection();
843    assert_eq!(term.get_selection(), None);
844
845    // Play with cursor position
846    term.append("0123456789\n"); // Set up test pattern
847    term.append("ABCDEFGHIJ\n");
848    term.append("abcdefghij\n");
849
850    term.set_cursor_row(1);
851    assert_eq!(term.cursor_row(), 1);
852    term.set_cursor_col(1);
853    assert_eq!(term.cursor_col(), 1);
854    assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856    term.append("----"); // Overwrites text at cursor and moves cursor forward
857    assert_eq!(term.cursor_row(), 1);
858    assert_eq!(term.cursor_col(), 5);
859    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860    term.set_cursor_col(1);
861    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863    term.cursor_up(1, false);
864    assert_eq!(term.cursor_row(), 0);
865    assert_eq!(term.cursor_col(), 1);
866    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868    // Hit top of screen, so nothing happens
869    term.cursor_up(1, false);
870    assert_eq!(term.cursor_row(), 0);
871    assert_eq!(term.cursor_col(), 1);
872    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874    // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875    term.cursor_up(1, true);
876    assert_eq!(term.cursor_row(), 0);
877    assert_eq!(term.cursor_col(), 1);
878    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880    // Go back down to the overwritten text
881    term.cursor_down(2, false);
882    assert_eq!(term.cursor_row(), 2);
883    assert_eq!(term.cursor_col(), 1);
884    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886    // Go right past the overwritten text
887    term.cursor_right(4, false);
888    assert_eq!(term.cursor_row(), 2);
889    assert_eq!(term.cursor_col(), 5);
890    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892    // Go left to the end of the overwritten text
893    term.cursor_left(1);
894    assert_eq!(term.cursor_row(), 2);
895    assert_eq!(term.cursor_col(), 4);
896    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898    // Scroll back down, removing the blank line at the top.
899    // Cursor stays in place, the text moves under it.
900    term.scroll(1);
901    assert_eq!(term.cursor_row(), 2);
902    assert_eq!(term.cursor_col(), 4);
903    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905    // Clear from here to end-of-line
906    term.clear_eol();
907    assert_eq!(term.cursor_row(), 2);
908    assert_eq!(term.cursor_col(), 4);
909    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911    // Now clear from here to start-of-line. Cursor does not move.
912    term.clear_sol();
913    assert_eq!(term.cursor_row(), 2);
914    assert_eq!(term.cursor_col(), 4);
915    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916    term.cursor_left(1);
917    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918    term.set_cursor_col(0);
919    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921    // Clear some lines
922    term.clear_line(1);
923    assert_eq!(term.cursor_row(), 2);
924    assert_eq!(term.cursor_col(), 0);
925    term.set_cursor_row(1);
926    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927    term.set_cursor_row(3);
928    term.clear_cur_line();
929    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930    assert_eq!(term.cursor_row(), 3);
931    assert_eq!(term.cursor_col(), 0);
932
933    term.append("Two lines above are intentionally left blank.\n");
934    assert_eq!(term.cursor_row(), 4);
935    assert_eq!(term.cursor_col(), 0);
936
937    // Set up the test pattern again, then play with insert/delete
938    term.append("0123456789\n");
939    term.append("ABCDEFGHIJ\n");
940    term.append("abcdefghij\n");
941    assert_eq!(term.cursor_row(), 7);
942
943    term.set_cursor_row(4);
944    term.set_cursor_col(4);
945    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947    term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949    term.cursor_right(5, false);
950    assert_eq!(term.cursor_col(), 9);
951    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953    // Insert two blank rows above cursor. Cursor stays put.
954    term.insert_rows(2);
955    assert_eq!(term.cursor_row(), 4);
956    assert_eq!(term.cursor_col(), 9);
957    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958    term.cursor_down(2, false); // Go down to find our text again
959    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961    // Go back to the beginning of the inserted 'x' characters and delete them.
962    term.cursor_left(5);
963    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964    term.delete_cur_chars(5);
965    assert_eq!(term.cursor_row(), 6);
966    assert_eq!(term.cursor_col(), 4);
967    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969    term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970    term.cursor_down(1, false);
971    term.cursor_left(2);
972    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974    term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975    assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976    term.cursor_up(1, false);
977    term.delete_rows(2); // Delete remains of test pattern
978
979    term.set_text_attrib(Attrib::Bold);
980    term.insert_char_eol('-', 3, 15, 20);
981    term.set_cursor_row(3);
982    term.set_cursor_col(15);
983    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984    assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986    term.set_text_attrib(Attrib::Italic);
987    term.append(" and all lines below");
988    term.set_text_attrib(Attrib::Normal);
989    term.cursor_down(1, false);
990
991    let mut hsb = term.hscrollbar();
992    let mut sb = term.scrollbar();
993    hsb.set_value(100.0);
994    assert_eq!(hsb.value(), 100.0);
995    sb.set_value(50.0);
996    assert_eq!(sb.value(), 50.0);
997    hsb.set_value(0.0);
998    assert_eq!(hsb.value(), 0.0);
999    sb.set_value(0.0);
1000    assert_eq!(sb.value(), 0.0);
1001}
Source

pub fn hscrollbar(&self) -> Scrollbar

Returns the horizontal scrollbar

Examples found in repository?
examples/terminal.rs (line 270)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
425//--------------------------------------------------------------------------------------
426/// More tests that run when the menu bar Test1 is clicked
427fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
428    term.take_focus().unwrap();
429    term.reset_terminal();
430    term.append("0123456789 0\n");
431    term.append("0123456789 1\n");
432    term.append("0123456789 2\n");
433    term.append("0123456789 3\n");
434    term.append("0123456789 4\n");
435    term.append("0123456789 5\n");
436    term.append("0123456789 6\n");
437    term.append("0123456789 7\n");
438    term.append("0123456789 8\n");
439    term.append("0123456789 9\n");
440    term.append("------------\n");
441
442    term.set_text_fg_color(Color::Green);
443    term.plot_char('A', 0, 0);
444    term.plot_char('B', 1, 1);
445    term.plot_char('C', 2, 2);
446    term.plot_char('D', 3, 3);
447    term.plot_char('E', 4, 4);
448    term.plot_char('F', 5, 5);
449    term.set_text_fg_color(Color::XtermWhite);
450
451    assert_eq!(term.cursor_row(), 11);
452    assert_eq!(term.cursor_col(), 0);
453
454    term.set_text_bg_color(Color::DarkBlue);
455    term.plot_char_utf8('b', 8, 1);
456    term.plot_char_utf8('↑', 9, 1);
457    term.plot_char_utf8('c', 8, 2);
458    term.plot_char_utf8('↑', 9, 2);
459    term.plot_char_utf8('d', 8, 3);
460    term.plot_char_utf8('↑', 9, 3);
461    term.plot_char_utf8('e', 8, 4);
462    term.plot_char_utf8('↑', 9, 4);
463    term.plot_char_utf8('f', 8, 5);
464    term.plot_char_utf8('↑', 9, 5);
465    term.plot_char_utf8('g', 8, 6);
466    term.plot_char_utf8('↑', 9, 6);
467    term.set_text_bg_color(Color::TransparentBg);
468
469    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
470    term.append("Done!\n");
471    term.set_text_attrib(Attrib::Normal);
472}
473
474//--------------------------------------------------------------------------------------
475/// More tests that run when the menu bar button Test2 is clicked
476fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
477    term.take_focus().unwrap();
478    term.reset_terminal();
479
480    for i in 0..50 {
481        term.append(&format!("{i}\n"));
482    }
483    assert_eq!(term.history_rows(), 100);
484
485    term.clear_history();
486    assert_eq!(term.history_use(), 0);
487
488    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
489    term.append("\nDone!\n");
490    term.set_text_attrib(Attrib::Normal);
491}
492
493//--------------------------------------------------------------------------------------
494/// Another set of tests that run when Test3 is clicked
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496    term.take_focus().unwrap();
497    term.reset_terminal();
498    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500    assert_eq!(term.history_use(), 0);
501    term.clear();
502    assert_eq!(term.cursor_row(), 0);
503    assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505    term.append("Test\ntext\na\nb\nc\nd");
506    assert_eq!(term.cursor_row(), 5);
507    let hist = term.history_use();
508    term.clear_screen_home(false);
509    assert_eq!(term.cursor_row(), 0);
510    assert_eq!(term.history_use(), hist); // History not changed
511
512    term.append("Test\ntext\na\nb\nc\nd\ne");
513    assert_eq!(term.cursor_row(), 6);
514    term.clear_screen_home(true);
515    assert_eq!(term.cursor_row(), 0);
516
517    term.append("Test\ntext\na\nb\nc\n");
518    assert_eq!(term.cursor_row(), 5);
519    term.clear_to_color(Color::DarkBlue);
520    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521    assert_eq!(term.text_bg_color(), Color::TransparentBg);
522    assert_eq!(term.cursor_row(), 0);
523
524    // Test cursor_home()
525    term.append("Test\n\n\n\n\n\n\n\n\n\n");
526    assert_eq!(term.cursor_row(), 10);
527    term.cursor_home();
528    assert_eq!(term.cursor_row(), 0);
529
530    // Test the widget color
531    assert_eq!(term.color(), Color::Black); // Default
532    term.set_color(Color::DarkGreen);
533    assert_eq!(term.color(), Color::DarkGreen);
534    term.set_color(Color::Black);
535    assert_eq!(term.color(), Color::Black);
536    term.append(
537        "This should be one line of white text on black, embedded into the top of a blue field.\n",
538    );
539
540    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541    term.set_output_translate(OutFlags::OFF);
542    assert_eq!(term.output_translate(), OutFlags::OFF);
543    term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547    term.append("\nDone!\n");
548    term.set_text_attrib(Attrib::Normal);
549}
550
551//--------------------------------------------------------------------------------------
552/// Another set of tests for the ring-buffer access methods
553/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555    let sel_len = term.selection_text_len();
556    let sel = term.selection_text();
557
558    term.take_focus().unwrap();
559    term.reset_terminal();
560    // Test the Utf8Char primitive
561    let uc = Utf8Char::new(b'Q');
562    let uc1 = uc.text_utf8();
563    assert_eq!(&uc1, b"Q");
564    assert_eq!(&uc.attrib(), &Attrib::Normal);
565    assert_eq!(
566        &uc.charflags(),
567        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568    );
569    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570    assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572    let ring_rows = term.ring_rows();
573
574    term.take_focus().unwrap();
575    term.clear_history();
576    assert_eq!(term.history_use(), 0);
577
578    // Subtract row numbers, modulo `rows`
579    fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580        match a - b {
581            n if n < 0 => n + rows,
582            n => n,
583        }
584    }
585    // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587    assert!(term.disp_srow() >= 0);
588    assert!(term.disp_erow() >= 0);
589    assert!(term.hist_srow() >= 0);
590    assert!(term.hist_erow() >= 0);
591    assert!(term.offset() >= 0);
592    assert!(term.disp_srow() <= ring_rows);
593    assert!(term.disp_erow() <= ring_rows);
594    assert!(term.hist_srow() <= ring_rows);
595    assert!(term.hist_erow() <= ring_rows);
596    assert!(term.offset() <= ring_rows);
597
598    assert_eq!(term.ring_srow(), 0);
599    assert_eq!(term.ring_erow(), ring_rows - 1);
600    assert_eq!(
601        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602        term.display_rows()
603    );
604    assert_eq!(
605        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606        term.history_rows()
607    );
608
609    assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610    assert_eq!(term.ring_srow(), 0);
611
612    /// Local function to read back all rows from the display into a long string.
613    /// Does not include scrollback history.
614    /// Trims trailing blanks on each line
615    fn read_disp(term: &Terminal) -> String {
616        let rows = term.display_rows();
617        let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618        for row in 0..rows {
619            let r = term.u8c_disp_row(row).trim();
620            // Iterate through a row, accumulating [u8]
621            for c in r.iter() {
622                // Note: Sometimes utf-8 length is > 1
623                text.extend_from_slice(c.text_utf8());
624            }
625            text.extend_from_slice(b"\n");
626        }
627        // Return the result as a string
628        std::str::from_utf8(&text).unwrap().to_string()
629    }
630
631    term.clear();
632    term.append("Top line  ↑ (up-arrow)");
633    term.set_text_attrib(Attrib::Underline);
634    term.append("  ");
635    term.set_text_attrib(Attrib::Normal);
636    term.append("  \n");
637    let mut text_out = read_disp(term);
638    // Trim trailing empty lines
639    text_out = text_out.trim_end_matches(&"\n").to_string();
640    // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642    assert_eq!(text_out, "Top line  ↑ (up-arrow)  ");
643    let r = term.u8c_disp_row(0);
644    assert_eq!(r.col(0).text_utf8(), b"T");
645    assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646    assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647    let r = term.u8c_disp_row(1);
648    assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649    assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651    // Clear the screen again, then append test text, then read it back and compare
652    let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656            Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659    term.clear_history();
660    term.clear();
661    let bg_save = term.text_bg_color();
662    let fg_save = term.text_fg_color();
663    term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664    term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665    term.append(test_text);
666    term.set_text_bg_color(bg_save);
667    term.set_text_fg_color(fg_save);
668
669    let mut text_out = read_disp(term);
670    // Trim trailing empty lines
671    text_out = text_out.trim_end_matches(&"\n").to_string();
672    assert_eq!(test_text, text_out);
673
674    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676    assert_eq!(term.ring_srow(), 0);
677    assert_eq!(term.ring_erow(), ring_rows - 1);
678    assert_eq!(
679        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680        term.display_rows()
681    );
682    assert_eq!(
683        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684        term.history_rows()
685    );
686
687    term.append(&format!(
688        "\n\nScreen has {} rows of {} columns.\n",
689        term.display_rows(),
690        term.display_columns()
691    ));
692
693    term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
695
696//--------------------------------------------------------------------------------------
697/// Yet another set of tests for misc cursor functions and other stuff
698/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700    term.take_focus().unwrap();
701
702    // Test the attr_fg_color and attr_bg_color methods.
703    // Put a single character 'A' into the buffer and check it
704    term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705    term.set_text_bg_color(Color::TransparentBg);
706    term.set_text_fg_color(Color::XtermWhite);
707    term.append("A");
708    let r = &term.u8c_disp_row(0);
709    let uc = r.col(0);
710    assert_eq!(uc.text_utf8(), b"A");
711    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712    assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713    assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714    assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715    assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717    // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718    term.clear();
719    term.set_text_fg_color_xterm(XtermColor::White);
720    term.set_text_bg_color_xterm(XtermColor::Black);
721    assert_eq!(term.text_attrib(), Attrib::Normal);
722
723    assert!(term.ansi());
724    term.append("B\x1b[32mC\x1b[1mD\n");
725
726    let r = &term.u8c_disp_row(0);
727    let uc = r.col(0);
728    assert_eq!(uc.text_utf8(), b"B");
729    assert!(uc.is_char(b'B'));
730    assert!(!uc.is_char(b'A'));
731    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735    assert_eq!(
736        &uc.charflags(),
737        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738    );
739
740    let uc = r.col(1);
741    assert_eq!(uc.text_utf8(), b"C");
742    assert!(uc.is_char(b'C'));
743    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747    assert_eq!(
748        &uc.charflags(),
749        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750    );
751
752    let uc = r.col(2);
753    assert_eq!(uc.text_utf8(), b"D");
754    assert!(uc.is_char(b'D'));
755    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757    assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759    assert_eq!(
760        &uc.attr_fgcolor(Some(term)),
761        &Color::from_rgb(0x20, 0xf0, 0x20)
762    );
763    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764    assert_eq!(
765        &uc.charflags(),
766        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767    );
768
769    // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770    term.clear();
771    term.set_text_fg_color_xterm(XtermColor::White);
772    term.set_text_bg_color_xterm(XtermColor::Black);
773    term.set_text_attrib(Attrib::Normal);
774    assert_eq!(term.text_attrib(), Attrib::Normal);
775
776    assert!(term.ansi());
777    term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779    let r = &term.u8c_disp_row(0);
780    let uc = r.col(0);
781    assert_eq!(uc.text_utf8(), b"B");
782    assert!(uc.is_char(b'B'));
783    assert!(!uc.is_char(b'A'));
784    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788    assert_eq!(
789        &uc.charflags(),
790        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791    );
792
793    let uc = r.col(1);
794    assert_eq!(uc.text_utf8(), b"C");
795    assert!(uc.is_char(b'C'));
796    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800    assert_eq!(
801        &uc.charflags(),
802        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803    );
804
805    let uc = r.col(2);
806    assert_eq!(uc.text_utf8(), b"D");
807    assert!(uc.is_char(b'D'));
808    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812    assert_eq!(
813        &uc.charflags(),
814        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815    );
816
817    let uc = r.col(3);
818    assert_eq!(uc.text_utf8(), b"E");
819    assert!(uc.is_char(b'E'));
820    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822    assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823    assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824    assert_eq!(
825        &uc.charflags(),
826        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827    );
828
829    // Test some miscellaneous Utf8 constants
830    assert_eq!(uc.length(), 1);
831    assert_eq!(uc.max_utf8(), 4);
832    assert_eq!(uc.pwidth(), 9.0);
833    assert_eq!(uc.pwidth_int(), 9);
834
835    term.set_text_fg_color_xterm(XtermColor::White);
836    term.set_text_bg_color_xterm(XtermColor::Black);
837    term.clear();
838    term.set_text_attrib(Attrib::Normal);
839
840    // Mouse selection functions
841    term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842    term.clear_mouse_selection();
843    assert_eq!(term.get_selection(), None);
844
845    // Play with cursor position
846    term.append("0123456789\n"); // Set up test pattern
847    term.append("ABCDEFGHIJ\n");
848    term.append("abcdefghij\n");
849
850    term.set_cursor_row(1);
851    assert_eq!(term.cursor_row(), 1);
852    term.set_cursor_col(1);
853    assert_eq!(term.cursor_col(), 1);
854    assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856    term.append("----"); // Overwrites text at cursor and moves cursor forward
857    assert_eq!(term.cursor_row(), 1);
858    assert_eq!(term.cursor_col(), 5);
859    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860    term.set_cursor_col(1);
861    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863    term.cursor_up(1, false);
864    assert_eq!(term.cursor_row(), 0);
865    assert_eq!(term.cursor_col(), 1);
866    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868    // Hit top of screen, so nothing happens
869    term.cursor_up(1, false);
870    assert_eq!(term.cursor_row(), 0);
871    assert_eq!(term.cursor_col(), 1);
872    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874    // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875    term.cursor_up(1, true);
876    assert_eq!(term.cursor_row(), 0);
877    assert_eq!(term.cursor_col(), 1);
878    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880    // Go back down to the overwritten text
881    term.cursor_down(2, false);
882    assert_eq!(term.cursor_row(), 2);
883    assert_eq!(term.cursor_col(), 1);
884    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886    // Go right past the overwritten text
887    term.cursor_right(4, false);
888    assert_eq!(term.cursor_row(), 2);
889    assert_eq!(term.cursor_col(), 5);
890    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892    // Go left to the end of the overwritten text
893    term.cursor_left(1);
894    assert_eq!(term.cursor_row(), 2);
895    assert_eq!(term.cursor_col(), 4);
896    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898    // Scroll back down, removing the blank line at the top.
899    // Cursor stays in place, the text moves under it.
900    term.scroll(1);
901    assert_eq!(term.cursor_row(), 2);
902    assert_eq!(term.cursor_col(), 4);
903    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905    // Clear from here to end-of-line
906    term.clear_eol();
907    assert_eq!(term.cursor_row(), 2);
908    assert_eq!(term.cursor_col(), 4);
909    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911    // Now clear from here to start-of-line. Cursor does not move.
912    term.clear_sol();
913    assert_eq!(term.cursor_row(), 2);
914    assert_eq!(term.cursor_col(), 4);
915    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916    term.cursor_left(1);
917    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918    term.set_cursor_col(0);
919    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921    // Clear some lines
922    term.clear_line(1);
923    assert_eq!(term.cursor_row(), 2);
924    assert_eq!(term.cursor_col(), 0);
925    term.set_cursor_row(1);
926    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927    term.set_cursor_row(3);
928    term.clear_cur_line();
929    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930    assert_eq!(term.cursor_row(), 3);
931    assert_eq!(term.cursor_col(), 0);
932
933    term.append("Two lines above are intentionally left blank.\n");
934    assert_eq!(term.cursor_row(), 4);
935    assert_eq!(term.cursor_col(), 0);
936
937    // Set up the test pattern again, then play with insert/delete
938    term.append("0123456789\n");
939    term.append("ABCDEFGHIJ\n");
940    term.append("abcdefghij\n");
941    assert_eq!(term.cursor_row(), 7);
942
943    term.set_cursor_row(4);
944    term.set_cursor_col(4);
945    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947    term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949    term.cursor_right(5, false);
950    assert_eq!(term.cursor_col(), 9);
951    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953    // Insert two blank rows above cursor. Cursor stays put.
954    term.insert_rows(2);
955    assert_eq!(term.cursor_row(), 4);
956    assert_eq!(term.cursor_col(), 9);
957    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958    term.cursor_down(2, false); // Go down to find our text again
959    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961    // Go back to the beginning of the inserted 'x' characters and delete them.
962    term.cursor_left(5);
963    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964    term.delete_cur_chars(5);
965    assert_eq!(term.cursor_row(), 6);
966    assert_eq!(term.cursor_col(), 4);
967    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969    term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970    term.cursor_down(1, false);
971    term.cursor_left(2);
972    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974    term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975    assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976    term.cursor_up(1, false);
977    term.delete_rows(2); // Delete remains of test pattern
978
979    term.set_text_attrib(Attrib::Bold);
980    term.insert_char_eol('-', 3, 15, 20);
981    term.set_cursor_row(3);
982    term.set_cursor_col(15);
983    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984    assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986    term.set_text_attrib(Attrib::Italic);
987    term.append(" and all lines below");
988    term.set_text_attrib(Attrib::Normal);
989    term.cursor_down(1, false);
990
991    let mut hsb = term.hscrollbar();
992    let mut sb = term.scrollbar();
993    hsb.set_value(100.0);
994    assert_eq!(hsb.value(), 100.0);
995    sb.set_value(50.0);
996    assert_eq!(sb.value(), 50.0);
997    hsb.set_value(0.0);
998    assert_eq!(hsb.value(), 0.0);
999    sb.set_value(0.0);
1000    assert_eq!(sb.value(), 0.0);
1001}
Source

pub fn hscrollbar_style(&self) -> ScrollbarStyle

Get the horizontal scrollbar behavior style.

This determines when the scrollbar is visible.

Value will be one of the Fl_Terminal::HScrollbarStyle enum values.

Examples found in repository?
examples/terminal.rs (line 131)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn set_hscrollbar_style(&mut self, val: ScrollbarStyle)

Set the scrollbar behavior style.

This determines when the scrollbar is visible.

ScrollbarStyle enumDescription
ONScrollbar is always displayed.
OFFScrollbar is never displayed.
AUTOScrollbar is displayed whenever the widget has been resized so that some of the text is hidden.

The default style is AUTO

Examples found in repository?
examples/terminal.rs (line 132)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn set_selection_bg_color(&mut self, color: Color)

Set mouse selection background color.

Examples found in repository?
examples/terminal.rs (line 304)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn selection_fg_color(&self) -> Color

Get mouse selection foreground color.

Examples found in repository?
examples/terminal.rs (line 298)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn set_selection_fg_color(&mut self, color: Color)

Set mouse selection foreground color.

Examples found in repository?
examples/terminal.rs (line 303)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn show_unknown(&self) -> bool

Return the “show unknown” flag. if true, show unknown chars as ‘¿’

Examples found in repository?
examples/terminal.rs (line 137)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn set_show_unknown(&mut self, arg1: bool)

Set the “show unknown” flag. if true, show unknown chars as ‘¿’ (default off)

Examples found in repository?
examples/terminal.rs (line 136)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn text_attrib(&self) -> Attrib

Return the text attribute bits (underline, inverse, etc) for subsequent appends.

Examples found in repository?
examples/terminal.rs (line 413)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
425//--------------------------------------------------------------------------------------
426/// More tests that run when the menu bar Test1 is clicked
427fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
428    term.take_focus().unwrap();
429    term.reset_terminal();
430    term.append("0123456789 0\n");
431    term.append("0123456789 1\n");
432    term.append("0123456789 2\n");
433    term.append("0123456789 3\n");
434    term.append("0123456789 4\n");
435    term.append("0123456789 5\n");
436    term.append("0123456789 6\n");
437    term.append("0123456789 7\n");
438    term.append("0123456789 8\n");
439    term.append("0123456789 9\n");
440    term.append("------------\n");
441
442    term.set_text_fg_color(Color::Green);
443    term.plot_char('A', 0, 0);
444    term.plot_char('B', 1, 1);
445    term.plot_char('C', 2, 2);
446    term.plot_char('D', 3, 3);
447    term.plot_char('E', 4, 4);
448    term.plot_char('F', 5, 5);
449    term.set_text_fg_color(Color::XtermWhite);
450
451    assert_eq!(term.cursor_row(), 11);
452    assert_eq!(term.cursor_col(), 0);
453
454    term.set_text_bg_color(Color::DarkBlue);
455    term.plot_char_utf8('b', 8, 1);
456    term.plot_char_utf8('↑', 9, 1);
457    term.plot_char_utf8('c', 8, 2);
458    term.plot_char_utf8('↑', 9, 2);
459    term.plot_char_utf8('d', 8, 3);
460    term.plot_char_utf8('↑', 9, 3);
461    term.plot_char_utf8('e', 8, 4);
462    term.plot_char_utf8('↑', 9, 4);
463    term.plot_char_utf8('f', 8, 5);
464    term.plot_char_utf8('↑', 9, 5);
465    term.plot_char_utf8('g', 8, 6);
466    term.plot_char_utf8('↑', 9, 6);
467    term.set_text_bg_color(Color::TransparentBg);
468
469    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
470    term.append("Done!\n");
471    term.set_text_attrib(Attrib::Normal);
472}
473
474//--------------------------------------------------------------------------------------
475/// More tests that run when the menu bar button Test2 is clicked
476fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
477    term.take_focus().unwrap();
478    term.reset_terminal();
479
480    for i in 0..50 {
481        term.append(&format!("{i}\n"));
482    }
483    assert_eq!(term.history_rows(), 100);
484
485    term.clear_history();
486    assert_eq!(term.history_use(), 0);
487
488    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
489    term.append("\nDone!\n");
490    term.set_text_attrib(Attrib::Normal);
491}
492
493//--------------------------------------------------------------------------------------
494/// Another set of tests that run when Test3 is clicked
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496    term.take_focus().unwrap();
497    term.reset_terminal();
498    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500    assert_eq!(term.history_use(), 0);
501    term.clear();
502    assert_eq!(term.cursor_row(), 0);
503    assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505    term.append("Test\ntext\na\nb\nc\nd");
506    assert_eq!(term.cursor_row(), 5);
507    let hist = term.history_use();
508    term.clear_screen_home(false);
509    assert_eq!(term.cursor_row(), 0);
510    assert_eq!(term.history_use(), hist); // History not changed
511
512    term.append("Test\ntext\na\nb\nc\nd\ne");
513    assert_eq!(term.cursor_row(), 6);
514    term.clear_screen_home(true);
515    assert_eq!(term.cursor_row(), 0);
516
517    term.append("Test\ntext\na\nb\nc\n");
518    assert_eq!(term.cursor_row(), 5);
519    term.clear_to_color(Color::DarkBlue);
520    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521    assert_eq!(term.text_bg_color(), Color::TransparentBg);
522    assert_eq!(term.cursor_row(), 0);
523
524    // Test cursor_home()
525    term.append("Test\n\n\n\n\n\n\n\n\n\n");
526    assert_eq!(term.cursor_row(), 10);
527    term.cursor_home();
528    assert_eq!(term.cursor_row(), 0);
529
530    // Test the widget color
531    assert_eq!(term.color(), Color::Black); // Default
532    term.set_color(Color::DarkGreen);
533    assert_eq!(term.color(), Color::DarkGreen);
534    term.set_color(Color::Black);
535    assert_eq!(term.color(), Color::Black);
536    term.append(
537        "This should be one line of white text on black, embedded into the top of a blue field.\n",
538    );
539
540    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541    term.set_output_translate(OutFlags::OFF);
542    assert_eq!(term.output_translate(), OutFlags::OFF);
543    term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547    term.append("\nDone!\n");
548    term.set_text_attrib(Attrib::Normal);
549}
550
551//--------------------------------------------------------------------------------------
552/// Another set of tests for the ring-buffer access methods
553/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555    let sel_len = term.selection_text_len();
556    let sel = term.selection_text();
557
558    term.take_focus().unwrap();
559    term.reset_terminal();
560    // Test the Utf8Char primitive
561    let uc = Utf8Char::new(b'Q');
562    let uc1 = uc.text_utf8();
563    assert_eq!(&uc1, b"Q");
564    assert_eq!(&uc.attrib(), &Attrib::Normal);
565    assert_eq!(
566        &uc.charflags(),
567        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568    );
569    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570    assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572    let ring_rows = term.ring_rows();
573
574    term.take_focus().unwrap();
575    term.clear_history();
576    assert_eq!(term.history_use(), 0);
577
578    // Subtract row numbers, modulo `rows`
579    fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580        match a - b {
581            n if n < 0 => n + rows,
582            n => n,
583        }
584    }
585    // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587    assert!(term.disp_srow() >= 0);
588    assert!(term.disp_erow() >= 0);
589    assert!(term.hist_srow() >= 0);
590    assert!(term.hist_erow() >= 0);
591    assert!(term.offset() >= 0);
592    assert!(term.disp_srow() <= ring_rows);
593    assert!(term.disp_erow() <= ring_rows);
594    assert!(term.hist_srow() <= ring_rows);
595    assert!(term.hist_erow() <= ring_rows);
596    assert!(term.offset() <= ring_rows);
597
598    assert_eq!(term.ring_srow(), 0);
599    assert_eq!(term.ring_erow(), ring_rows - 1);
600    assert_eq!(
601        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602        term.display_rows()
603    );
604    assert_eq!(
605        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606        term.history_rows()
607    );
608
609    assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610    assert_eq!(term.ring_srow(), 0);
611
612    /// Local function to read back all rows from the display into a long string.
613    /// Does not include scrollback history.
614    /// Trims trailing blanks on each line
615    fn read_disp(term: &Terminal) -> String {
616        let rows = term.display_rows();
617        let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618        for row in 0..rows {
619            let r = term.u8c_disp_row(row).trim();
620            // Iterate through a row, accumulating [u8]
621            for c in r.iter() {
622                // Note: Sometimes utf-8 length is > 1
623                text.extend_from_slice(c.text_utf8());
624            }
625            text.extend_from_slice(b"\n");
626        }
627        // Return the result as a string
628        std::str::from_utf8(&text).unwrap().to_string()
629    }
630
631    term.clear();
632    term.append("Top line  ↑ (up-arrow)");
633    term.set_text_attrib(Attrib::Underline);
634    term.append("  ");
635    term.set_text_attrib(Attrib::Normal);
636    term.append("  \n");
637    let mut text_out = read_disp(term);
638    // Trim trailing empty lines
639    text_out = text_out.trim_end_matches(&"\n").to_string();
640    // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642    assert_eq!(text_out, "Top line  ↑ (up-arrow)  ");
643    let r = term.u8c_disp_row(0);
644    assert_eq!(r.col(0).text_utf8(), b"T");
645    assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646    assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647    let r = term.u8c_disp_row(1);
648    assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649    assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651    // Clear the screen again, then append test text, then read it back and compare
652    let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656            Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659    term.clear_history();
660    term.clear();
661    let bg_save = term.text_bg_color();
662    let fg_save = term.text_fg_color();
663    term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664    term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665    term.append(test_text);
666    term.set_text_bg_color(bg_save);
667    term.set_text_fg_color(fg_save);
668
669    let mut text_out = read_disp(term);
670    // Trim trailing empty lines
671    text_out = text_out.trim_end_matches(&"\n").to_string();
672    assert_eq!(test_text, text_out);
673
674    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676    assert_eq!(term.ring_srow(), 0);
677    assert_eq!(term.ring_erow(), ring_rows - 1);
678    assert_eq!(
679        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680        term.display_rows()
681    );
682    assert_eq!(
683        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684        term.history_rows()
685    );
686
687    term.append(&format!(
688        "\n\nScreen has {} rows of {} columns.\n",
689        term.display_rows(),
690        term.display_columns()
691    ));
692
693    term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
695
696//--------------------------------------------------------------------------------------
697/// Yet another set of tests for misc cursor functions and other stuff
698/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700    term.take_focus().unwrap();
701
702    // Test the attr_fg_color and attr_bg_color methods.
703    // Put a single character 'A' into the buffer and check it
704    term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705    term.set_text_bg_color(Color::TransparentBg);
706    term.set_text_fg_color(Color::XtermWhite);
707    term.append("A");
708    let r = &term.u8c_disp_row(0);
709    let uc = r.col(0);
710    assert_eq!(uc.text_utf8(), b"A");
711    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712    assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713    assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714    assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715    assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717    // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718    term.clear();
719    term.set_text_fg_color_xterm(XtermColor::White);
720    term.set_text_bg_color_xterm(XtermColor::Black);
721    assert_eq!(term.text_attrib(), Attrib::Normal);
722
723    assert!(term.ansi());
724    term.append("B\x1b[32mC\x1b[1mD\n");
725
726    let r = &term.u8c_disp_row(0);
727    let uc = r.col(0);
728    assert_eq!(uc.text_utf8(), b"B");
729    assert!(uc.is_char(b'B'));
730    assert!(!uc.is_char(b'A'));
731    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735    assert_eq!(
736        &uc.charflags(),
737        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738    );
739
740    let uc = r.col(1);
741    assert_eq!(uc.text_utf8(), b"C");
742    assert!(uc.is_char(b'C'));
743    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747    assert_eq!(
748        &uc.charflags(),
749        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750    );
751
752    let uc = r.col(2);
753    assert_eq!(uc.text_utf8(), b"D");
754    assert!(uc.is_char(b'D'));
755    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757    assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759    assert_eq!(
760        &uc.attr_fgcolor(Some(term)),
761        &Color::from_rgb(0x20, 0xf0, 0x20)
762    );
763    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764    assert_eq!(
765        &uc.charflags(),
766        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767    );
768
769    // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770    term.clear();
771    term.set_text_fg_color_xterm(XtermColor::White);
772    term.set_text_bg_color_xterm(XtermColor::Black);
773    term.set_text_attrib(Attrib::Normal);
774    assert_eq!(term.text_attrib(), Attrib::Normal);
775
776    assert!(term.ansi());
777    term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779    let r = &term.u8c_disp_row(0);
780    let uc = r.col(0);
781    assert_eq!(uc.text_utf8(), b"B");
782    assert!(uc.is_char(b'B'));
783    assert!(!uc.is_char(b'A'));
784    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788    assert_eq!(
789        &uc.charflags(),
790        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791    );
792
793    let uc = r.col(1);
794    assert_eq!(uc.text_utf8(), b"C");
795    assert!(uc.is_char(b'C'));
796    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800    assert_eq!(
801        &uc.charflags(),
802        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803    );
804
805    let uc = r.col(2);
806    assert_eq!(uc.text_utf8(), b"D");
807    assert!(uc.is_char(b'D'));
808    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812    assert_eq!(
813        &uc.charflags(),
814        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815    );
816
817    let uc = r.col(3);
818    assert_eq!(uc.text_utf8(), b"E");
819    assert!(uc.is_char(b'E'));
820    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822    assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823    assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824    assert_eq!(
825        &uc.charflags(),
826        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827    );
828
829    // Test some miscellaneous Utf8 constants
830    assert_eq!(uc.length(), 1);
831    assert_eq!(uc.max_utf8(), 4);
832    assert_eq!(uc.pwidth(), 9.0);
833    assert_eq!(uc.pwidth_int(), 9);
834
835    term.set_text_fg_color_xterm(XtermColor::White);
836    term.set_text_bg_color_xterm(XtermColor::Black);
837    term.clear();
838    term.set_text_attrib(Attrib::Normal);
839
840    // Mouse selection functions
841    term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842    term.clear_mouse_selection();
843    assert_eq!(term.get_selection(), None);
844
845    // Play with cursor position
846    term.append("0123456789\n"); // Set up test pattern
847    term.append("ABCDEFGHIJ\n");
848    term.append("abcdefghij\n");
849
850    term.set_cursor_row(1);
851    assert_eq!(term.cursor_row(), 1);
852    term.set_cursor_col(1);
853    assert_eq!(term.cursor_col(), 1);
854    assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856    term.append("----"); // Overwrites text at cursor and moves cursor forward
857    assert_eq!(term.cursor_row(), 1);
858    assert_eq!(term.cursor_col(), 5);
859    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860    term.set_cursor_col(1);
861    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863    term.cursor_up(1, false);
864    assert_eq!(term.cursor_row(), 0);
865    assert_eq!(term.cursor_col(), 1);
866    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868    // Hit top of screen, so nothing happens
869    term.cursor_up(1, false);
870    assert_eq!(term.cursor_row(), 0);
871    assert_eq!(term.cursor_col(), 1);
872    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874    // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875    term.cursor_up(1, true);
876    assert_eq!(term.cursor_row(), 0);
877    assert_eq!(term.cursor_col(), 1);
878    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880    // Go back down to the overwritten text
881    term.cursor_down(2, false);
882    assert_eq!(term.cursor_row(), 2);
883    assert_eq!(term.cursor_col(), 1);
884    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886    // Go right past the overwritten text
887    term.cursor_right(4, false);
888    assert_eq!(term.cursor_row(), 2);
889    assert_eq!(term.cursor_col(), 5);
890    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892    // Go left to the end of the overwritten text
893    term.cursor_left(1);
894    assert_eq!(term.cursor_row(), 2);
895    assert_eq!(term.cursor_col(), 4);
896    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898    // Scroll back down, removing the blank line at the top.
899    // Cursor stays in place, the text moves under it.
900    term.scroll(1);
901    assert_eq!(term.cursor_row(), 2);
902    assert_eq!(term.cursor_col(), 4);
903    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905    // Clear from here to end-of-line
906    term.clear_eol();
907    assert_eq!(term.cursor_row(), 2);
908    assert_eq!(term.cursor_col(), 4);
909    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911    // Now clear from here to start-of-line. Cursor does not move.
912    term.clear_sol();
913    assert_eq!(term.cursor_row(), 2);
914    assert_eq!(term.cursor_col(), 4);
915    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916    term.cursor_left(1);
917    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918    term.set_cursor_col(0);
919    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921    // Clear some lines
922    term.clear_line(1);
923    assert_eq!(term.cursor_row(), 2);
924    assert_eq!(term.cursor_col(), 0);
925    term.set_cursor_row(1);
926    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927    term.set_cursor_row(3);
928    term.clear_cur_line();
929    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930    assert_eq!(term.cursor_row(), 3);
931    assert_eq!(term.cursor_col(), 0);
932
933    term.append("Two lines above are intentionally left blank.\n");
934    assert_eq!(term.cursor_row(), 4);
935    assert_eq!(term.cursor_col(), 0);
936
937    // Set up the test pattern again, then play with insert/delete
938    term.append("0123456789\n");
939    term.append("ABCDEFGHIJ\n");
940    term.append("abcdefghij\n");
941    assert_eq!(term.cursor_row(), 7);
942
943    term.set_cursor_row(4);
944    term.set_cursor_col(4);
945    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947    term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949    term.cursor_right(5, false);
950    assert_eq!(term.cursor_col(), 9);
951    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953    // Insert two blank rows above cursor. Cursor stays put.
954    term.insert_rows(2);
955    assert_eq!(term.cursor_row(), 4);
956    assert_eq!(term.cursor_col(), 9);
957    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958    term.cursor_down(2, false); // Go down to find our text again
959    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961    // Go back to the beginning of the inserted 'x' characters and delete them.
962    term.cursor_left(5);
963    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964    term.delete_cur_chars(5);
965    assert_eq!(term.cursor_row(), 6);
966    assert_eq!(term.cursor_col(), 4);
967    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969    term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970    term.cursor_down(1, false);
971    term.cursor_left(2);
972    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974    term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975    assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976    term.cursor_up(1, false);
977    term.delete_rows(2); // Delete remains of test pattern
978
979    term.set_text_attrib(Attrib::Bold);
980    term.insert_char_eol('-', 3, 15, 20);
981    term.set_cursor_row(3);
982    term.set_cursor_col(15);
983    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984    assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986    term.set_text_attrib(Attrib::Italic);
987    term.append(" and all lines below");
988    term.set_text_attrib(Attrib::Normal);
989    term.cursor_down(1, false);
990
991    let mut hsb = term.hscrollbar();
992    let mut sb = term.scrollbar();
993    hsb.set_value(100.0);
994    assert_eq!(hsb.value(), 100.0);
995    sb.set_value(50.0);
996    assert_eq!(sb.value(), 50.0);
997    hsb.set_value(0.0);
998    assert_eq!(hsb.value(), 0.0);
999    sb.set_value(0.0);
1000    assert_eq!(sb.value(), 0.0);
1001}
Source

pub fn set_text_attrib(&mut self, arg1: Attrib)

Set text attribute bits (underline, inverse, etc) for subsequent appends.

Examples found in repository?
examples/terminal.rs (line 414)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
425//--------------------------------------------------------------------------------------
426/// More tests that run when the menu bar Test1 is clicked
427fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
428    term.take_focus().unwrap();
429    term.reset_terminal();
430    term.append("0123456789 0\n");
431    term.append("0123456789 1\n");
432    term.append("0123456789 2\n");
433    term.append("0123456789 3\n");
434    term.append("0123456789 4\n");
435    term.append("0123456789 5\n");
436    term.append("0123456789 6\n");
437    term.append("0123456789 7\n");
438    term.append("0123456789 8\n");
439    term.append("0123456789 9\n");
440    term.append("------------\n");
441
442    term.set_text_fg_color(Color::Green);
443    term.plot_char('A', 0, 0);
444    term.plot_char('B', 1, 1);
445    term.plot_char('C', 2, 2);
446    term.plot_char('D', 3, 3);
447    term.plot_char('E', 4, 4);
448    term.plot_char('F', 5, 5);
449    term.set_text_fg_color(Color::XtermWhite);
450
451    assert_eq!(term.cursor_row(), 11);
452    assert_eq!(term.cursor_col(), 0);
453
454    term.set_text_bg_color(Color::DarkBlue);
455    term.plot_char_utf8('b', 8, 1);
456    term.plot_char_utf8('↑', 9, 1);
457    term.plot_char_utf8('c', 8, 2);
458    term.plot_char_utf8('↑', 9, 2);
459    term.plot_char_utf8('d', 8, 3);
460    term.plot_char_utf8('↑', 9, 3);
461    term.plot_char_utf8('e', 8, 4);
462    term.plot_char_utf8('↑', 9, 4);
463    term.plot_char_utf8('f', 8, 5);
464    term.plot_char_utf8('↑', 9, 5);
465    term.plot_char_utf8('g', 8, 6);
466    term.plot_char_utf8('↑', 9, 6);
467    term.set_text_bg_color(Color::TransparentBg);
468
469    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
470    term.append("Done!\n");
471    term.set_text_attrib(Attrib::Normal);
472}
473
474//--------------------------------------------------------------------------------------
475/// More tests that run when the menu bar button Test2 is clicked
476fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
477    term.take_focus().unwrap();
478    term.reset_terminal();
479
480    for i in 0..50 {
481        term.append(&format!("{i}\n"));
482    }
483    assert_eq!(term.history_rows(), 100);
484
485    term.clear_history();
486    assert_eq!(term.history_use(), 0);
487
488    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
489    term.append("\nDone!\n");
490    term.set_text_attrib(Attrib::Normal);
491}
492
493//--------------------------------------------------------------------------------------
494/// Another set of tests that run when Test3 is clicked
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496    term.take_focus().unwrap();
497    term.reset_terminal();
498    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500    assert_eq!(term.history_use(), 0);
501    term.clear();
502    assert_eq!(term.cursor_row(), 0);
503    assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505    term.append("Test\ntext\na\nb\nc\nd");
506    assert_eq!(term.cursor_row(), 5);
507    let hist = term.history_use();
508    term.clear_screen_home(false);
509    assert_eq!(term.cursor_row(), 0);
510    assert_eq!(term.history_use(), hist); // History not changed
511
512    term.append("Test\ntext\na\nb\nc\nd\ne");
513    assert_eq!(term.cursor_row(), 6);
514    term.clear_screen_home(true);
515    assert_eq!(term.cursor_row(), 0);
516
517    term.append("Test\ntext\na\nb\nc\n");
518    assert_eq!(term.cursor_row(), 5);
519    term.clear_to_color(Color::DarkBlue);
520    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521    assert_eq!(term.text_bg_color(), Color::TransparentBg);
522    assert_eq!(term.cursor_row(), 0);
523
524    // Test cursor_home()
525    term.append("Test\n\n\n\n\n\n\n\n\n\n");
526    assert_eq!(term.cursor_row(), 10);
527    term.cursor_home();
528    assert_eq!(term.cursor_row(), 0);
529
530    // Test the widget color
531    assert_eq!(term.color(), Color::Black); // Default
532    term.set_color(Color::DarkGreen);
533    assert_eq!(term.color(), Color::DarkGreen);
534    term.set_color(Color::Black);
535    assert_eq!(term.color(), Color::Black);
536    term.append(
537        "This should be one line of white text on black, embedded into the top of a blue field.\n",
538    );
539
540    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541    term.set_output_translate(OutFlags::OFF);
542    assert_eq!(term.output_translate(), OutFlags::OFF);
543    term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547    term.append("\nDone!\n");
548    term.set_text_attrib(Attrib::Normal);
549}
550
551//--------------------------------------------------------------------------------------
552/// Another set of tests for the ring-buffer access methods
553/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555    let sel_len = term.selection_text_len();
556    let sel = term.selection_text();
557
558    term.take_focus().unwrap();
559    term.reset_terminal();
560    // Test the Utf8Char primitive
561    let uc = Utf8Char::new(b'Q');
562    let uc1 = uc.text_utf8();
563    assert_eq!(&uc1, b"Q");
564    assert_eq!(&uc.attrib(), &Attrib::Normal);
565    assert_eq!(
566        &uc.charflags(),
567        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568    );
569    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570    assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572    let ring_rows = term.ring_rows();
573
574    term.take_focus().unwrap();
575    term.clear_history();
576    assert_eq!(term.history_use(), 0);
577
578    // Subtract row numbers, modulo `rows`
579    fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580        match a - b {
581            n if n < 0 => n + rows,
582            n => n,
583        }
584    }
585    // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587    assert!(term.disp_srow() >= 0);
588    assert!(term.disp_erow() >= 0);
589    assert!(term.hist_srow() >= 0);
590    assert!(term.hist_erow() >= 0);
591    assert!(term.offset() >= 0);
592    assert!(term.disp_srow() <= ring_rows);
593    assert!(term.disp_erow() <= ring_rows);
594    assert!(term.hist_srow() <= ring_rows);
595    assert!(term.hist_erow() <= ring_rows);
596    assert!(term.offset() <= ring_rows);
597
598    assert_eq!(term.ring_srow(), 0);
599    assert_eq!(term.ring_erow(), ring_rows - 1);
600    assert_eq!(
601        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602        term.display_rows()
603    );
604    assert_eq!(
605        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606        term.history_rows()
607    );
608
609    assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610    assert_eq!(term.ring_srow(), 0);
611
612    /// Local function to read back all rows from the display into a long string.
613    /// Does not include scrollback history.
614    /// Trims trailing blanks on each line
615    fn read_disp(term: &Terminal) -> String {
616        let rows = term.display_rows();
617        let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618        for row in 0..rows {
619            let r = term.u8c_disp_row(row).trim();
620            // Iterate through a row, accumulating [u8]
621            for c in r.iter() {
622                // Note: Sometimes utf-8 length is > 1
623                text.extend_from_slice(c.text_utf8());
624            }
625            text.extend_from_slice(b"\n");
626        }
627        // Return the result as a string
628        std::str::from_utf8(&text).unwrap().to_string()
629    }
630
631    term.clear();
632    term.append("Top line  ↑ (up-arrow)");
633    term.set_text_attrib(Attrib::Underline);
634    term.append("  ");
635    term.set_text_attrib(Attrib::Normal);
636    term.append("  \n");
637    let mut text_out = read_disp(term);
638    // Trim trailing empty lines
639    text_out = text_out.trim_end_matches(&"\n").to_string();
640    // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642    assert_eq!(text_out, "Top line  ↑ (up-arrow)  ");
643    let r = term.u8c_disp_row(0);
644    assert_eq!(r.col(0).text_utf8(), b"T");
645    assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646    assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647    let r = term.u8c_disp_row(1);
648    assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649    assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651    // Clear the screen again, then append test text, then read it back and compare
652    let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656            Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659    term.clear_history();
660    term.clear();
661    let bg_save = term.text_bg_color();
662    let fg_save = term.text_fg_color();
663    term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664    term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665    term.append(test_text);
666    term.set_text_bg_color(bg_save);
667    term.set_text_fg_color(fg_save);
668
669    let mut text_out = read_disp(term);
670    // Trim trailing empty lines
671    text_out = text_out.trim_end_matches(&"\n").to_string();
672    assert_eq!(test_text, text_out);
673
674    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676    assert_eq!(term.ring_srow(), 0);
677    assert_eq!(term.ring_erow(), ring_rows - 1);
678    assert_eq!(
679        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680        term.display_rows()
681    );
682    assert_eq!(
683        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684        term.history_rows()
685    );
686
687    term.append(&format!(
688        "\n\nScreen has {} rows of {} columns.\n",
689        term.display_rows(),
690        term.display_columns()
691    ));
692
693    term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
695
696//--------------------------------------------------------------------------------------
697/// Yet another set of tests for misc cursor functions and other stuff
698/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700    term.take_focus().unwrap();
701
702    // Test the attr_fg_color and attr_bg_color methods.
703    // Put a single character 'A' into the buffer and check it
704    term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705    term.set_text_bg_color(Color::TransparentBg);
706    term.set_text_fg_color(Color::XtermWhite);
707    term.append("A");
708    let r = &term.u8c_disp_row(0);
709    let uc = r.col(0);
710    assert_eq!(uc.text_utf8(), b"A");
711    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712    assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713    assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714    assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715    assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717    // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718    term.clear();
719    term.set_text_fg_color_xterm(XtermColor::White);
720    term.set_text_bg_color_xterm(XtermColor::Black);
721    assert_eq!(term.text_attrib(), Attrib::Normal);
722
723    assert!(term.ansi());
724    term.append("B\x1b[32mC\x1b[1mD\n");
725
726    let r = &term.u8c_disp_row(0);
727    let uc = r.col(0);
728    assert_eq!(uc.text_utf8(), b"B");
729    assert!(uc.is_char(b'B'));
730    assert!(!uc.is_char(b'A'));
731    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735    assert_eq!(
736        &uc.charflags(),
737        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738    );
739
740    let uc = r.col(1);
741    assert_eq!(uc.text_utf8(), b"C");
742    assert!(uc.is_char(b'C'));
743    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747    assert_eq!(
748        &uc.charflags(),
749        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750    );
751
752    let uc = r.col(2);
753    assert_eq!(uc.text_utf8(), b"D");
754    assert!(uc.is_char(b'D'));
755    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757    assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759    assert_eq!(
760        &uc.attr_fgcolor(Some(term)),
761        &Color::from_rgb(0x20, 0xf0, 0x20)
762    );
763    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764    assert_eq!(
765        &uc.charflags(),
766        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767    );
768
769    // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770    term.clear();
771    term.set_text_fg_color_xterm(XtermColor::White);
772    term.set_text_bg_color_xterm(XtermColor::Black);
773    term.set_text_attrib(Attrib::Normal);
774    assert_eq!(term.text_attrib(), Attrib::Normal);
775
776    assert!(term.ansi());
777    term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779    let r = &term.u8c_disp_row(0);
780    let uc = r.col(0);
781    assert_eq!(uc.text_utf8(), b"B");
782    assert!(uc.is_char(b'B'));
783    assert!(!uc.is_char(b'A'));
784    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788    assert_eq!(
789        &uc.charflags(),
790        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791    );
792
793    let uc = r.col(1);
794    assert_eq!(uc.text_utf8(), b"C");
795    assert!(uc.is_char(b'C'));
796    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800    assert_eq!(
801        &uc.charflags(),
802        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803    );
804
805    let uc = r.col(2);
806    assert_eq!(uc.text_utf8(), b"D");
807    assert!(uc.is_char(b'D'));
808    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812    assert_eq!(
813        &uc.charflags(),
814        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815    );
816
817    let uc = r.col(3);
818    assert_eq!(uc.text_utf8(), b"E");
819    assert!(uc.is_char(b'E'));
820    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822    assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823    assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824    assert_eq!(
825        &uc.charflags(),
826        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827    );
828
829    // Test some miscellaneous Utf8 constants
830    assert_eq!(uc.length(), 1);
831    assert_eq!(uc.max_utf8(), 4);
832    assert_eq!(uc.pwidth(), 9.0);
833    assert_eq!(uc.pwidth_int(), 9);
834
835    term.set_text_fg_color_xterm(XtermColor::White);
836    term.set_text_bg_color_xterm(XtermColor::Black);
837    term.clear();
838    term.set_text_attrib(Attrib::Normal);
839
840    // Mouse selection functions
841    term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842    term.clear_mouse_selection();
843    assert_eq!(term.get_selection(), None);
844
845    // Play with cursor position
846    term.append("0123456789\n"); // Set up test pattern
847    term.append("ABCDEFGHIJ\n");
848    term.append("abcdefghij\n");
849
850    term.set_cursor_row(1);
851    assert_eq!(term.cursor_row(), 1);
852    term.set_cursor_col(1);
853    assert_eq!(term.cursor_col(), 1);
854    assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856    term.append("----"); // Overwrites text at cursor and moves cursor forward
857    assert_eq!(term.cursor_row(), 1);
858    assert_eq!(term.cursor_col(), 5);
859    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860    term.set_cursor_col(1);
861    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863    term.cursor_up(1, false);
864    assert_eq!(term.cursor_row(), 0);
865    assert_eq!(term.cursor_col(), 1);
866    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868    // Hit top of screen, so nothing happens
869    term.cursor_up(1, false);
870    assert_eq!(term.cursor_row(), 0);
871    assert_eq!(term.cursor_col(), 1);
872    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874    // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875    term.cursor_up(1, true);
876    assert_eq!(term.cursor_row(), 0);
877    assert_eq!(term.cursor_col(), 1);
878    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880    // Go back down to the overwritten text
881    term.cursor_down(2, false);
882    assert_eq!(term.cursor_row(), 2);
883    assert_eq!(term.cursor_col(), 1);
884    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886    // Go right past the overwritten text
887    term.cursor_right(4, false);
888    assert_eq!(term.cursor_row(), 2);
889    assert_eq!(term.cursor_col(), 5);
890    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892    // Go left to the end of the overwritten text
893    term.cursor_left(1);
894    assert_eq!(term.cursor_row(), 2);
895    assert_eq!(term.cursor_col(), 4);
896    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898    // Scroll back down, removing the blank line at the top.
899    // Cursor stays in place, the text moves under it.
900    term.scroll(1);
901    assert_eq!(term.cursor_row(), 2);
902    assert_eq!(term.cursor_col(), 4);
903    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905    // Clear from here to end-of-line
906    term.clear_eol();
907    assert_eq!(term.cursor_row(), 2);
908    assert_eq!(term.cursor_col(), 4);
909    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911    // Now clear from here to start-of-line. Cursor does not move.
912    term.clear_sol();
913    assert_eq!(term.cursor_row(), 2);
914    assert_eq!(term.cursor_col(), 4);
915    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916    term.cursor_left(1);
917    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918    term.set_cursor_col(0);
919    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921    // Clear some lines
922    term.clear_line(1);
923    assert_eq!(term.cursor_row(), 2);
924    assert_eq!(term.cursor_col(), 0);
925    term.set_cursor_row(1);
926    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927    term.set_cursor_row(3);
928    term.clear_cur_line();
929    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930    assert_eq!(term.cursor_row(), 3);
931    assert_eq!(term.cursor_col(), 0);
932
933    term.append("Two lines above are intentionally left blank.\n");
934    assert_eq!(term.cursor_row(), 4);
935    assert_eq!(term.cursor_col(), 0);
936
937    // Set up the test pattern again, then play with insert/delete
938    term.append("0123456789\n");
939    term.append("ABCDEFGHIJ\n");
940    term.append("abcdefghij\n");
941    assert_eq!(term.cursor_row(), 7);
942
943    term.set_cursor_row(4);
944    term.set_cursor_col(4);
945    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947    term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949    term.cursor_right(5, false);
950    assert_eq!(term.cursor_col(), 9);
951    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953    // Insert two blank rows above cursor. Cursor stays put.
954    term.insert_rows(2);
955    assert_eq!(term.cursor_row(), 4);
956    assert_eq!(term.cursor_col(), 9);
957    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958    term.cursor_down(2, false); // Go down to find our text again
959    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961    // Go back to the beginning of the inserted 'x' characters and delete them.
962    term.cursor_left(5);
963    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964    term.delete_cur_chars(5);
965    assert_eq!(term.cursor_row(), 6);
966    assert_eq!(term.cursor_col(), 4);
967    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969    term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970    term.cursor_down(1, false);
971    term.cursor_left(2);
972    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974    term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975    assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976    term.cursor_up(1, false);
977    term.delete_rows(2); // Delete remains of test pattern
978
979    term.set_text_attrib(Attrib::Bold);
980    term.insert_char_eol('-', 3, 15, 20);
981    term.set_cursor_row(3);
982    term.set_cursor_col(15);
983    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984    assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986    term.set_text_attrib(Attrib::Italic);
987    term.append(" and all lines below");
988    term.set_text_attrib(Attrib::Normal);
989    term.cursor_down(1, false);
990
991    let mut hsb = term.hscrollbar();
992    let mut sb = term.scrollbar();
993    hsb.set_value(100.0);
994    assert_eq!(hsb.value(), 100.0);
995    sb.set_value(50.0);
996    assert_eq!(sb.value(), 50.0);
997    hsb.set_value(0.0);
998    assert_eq!(hsb.value(), 0.0);
999    sb.set_value(0.0);
1000    assert_eq!(sb.value(), 0.0);
1001}
Source

pub fn set_text_bg_color(&mut self, color: Color)

Set text background color to fltk color val. Use this for temporary color changes, similar to <ESC>[48;2;{R};{G};{B}m

This setting does not affect the ‘default’ text colors used by <ESC>[0m, <ESC>c, reset_terminal(), etc. To change both the current and default bg color, also use text_bg_color_default(Fl_Color).

Examples found in repository?
examples/terminal.rs (line 332)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
425//--------------------------------------------------------------------------------------
426/// More tests that run when the menu bar Test1 is clicked
427fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
428    term.take_focus().unwrap();
429    term.reset_terminal();
430    term.append("0123456789 0\n");
431    term.append("0123456789 1\n");
432    term.append("0123456789 2\n");
433    term.append("0123456789 3\n");
434    term.append("0123456789 4\n");
435    term.append("0123456789 5\n");
436    term.append("0123456789 6\n");
437    term.append("0123456789 7\n");
438    term.append("0123456789 8\n");
439    term.append("0123456789 9\n");
440    term.append("------------\n");
441
442    term.set_text_fg_color(Color::Green);
443    term.plot_char('A', 0, 0);
444    term.plot_char('B', 1, 1);
445    term.plot_char('C', 2, 2);
446    term.plot_char('D', 3, 3);
447    term.plot_char('E', 4, 4);
448    term.plot_char('F', 5, 5);
449    term.set_text_fg_color(Color::XtermWhite);
450
451    assert_eq!(term.cursor_row(), 11);
452    assert_eq!(term.cursor_col(), 0);
453
454    term.set_text_bg_color(Color::DarkBlue);
455    term.plot_char_utf8('b', 8, 1);
456    term.plot_char_utf8('↑', 9, 1);
457    term.plot_char_utf8('c', 8, 2);
458    term.plot_char_utf8('↑', 9, 2);
459    term.plot_char_utf8('d', 8, 3);
460    term.plot_char_utf8('↑', 9, 3);
461    term.plot_char_utf8('e', 8, 4);
462    term.plot_char_utf8('↑', 9, 4);
463    term.plot_char_utf8('f', 8, 5);
464    term.plot_char_utf8('↑', 9, 5);
465    term.plot_char_utf8('g', 8, 6);
466    term.plot_char_utf8('↑', 9, 6);
467    term.set_text_bg_color(Color::TransparentBg);
468
469    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
470    term.append("Done!\n");
471    term.set_text_attrib(Attrib::Normal);
472}
473
474//--------------------------------------------------------------------------------------
475/// More tests that run when the menu bar button Test2 is clicked
476fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
477    term.take_focus().unwrap();
478    term.reset_terminal();
479
480    for i in 0..50 {
481        term.append(&format!("{i}\n"));
482    }
483    assert_eq!(term.history_rows(), 100);
484
485    term.clear_history();
486    assert_eq!(term.history_use(), 0);
487
488    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
489    term.append("\nDone!\n");
490    term.set_text_attrib(Attrib::Normal);
491}
492
493//--------------------------------------------------------------------------------------
494/// Another set of tests that run when Test3 is clicked
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496    term.take_focus().unwrap();
497    term.reset_terminal();
498    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500    assert_eq!(term.history_use(), 0);
501    term.clear();
502    assert_eq!(term.cursor_row(), 0);
503    assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505    term.append("Test\ntext\na\nb\nc\nd");
506    assert_eq!(term.cursor_row(), 5);
507    let hist = term.history_use();
508    term.clear_screen_home(false);
509    assert_eq!(term.cursor_row(), 0);
510    assert_eq!(term.history_use(), hist); // History not changed
511
512    term.append("Test\ntext\na\nb\nc\nd\ne");
513    assert_eq!(term.cursor_row(), 6);
514    term.clear_screen_home(true);
515    assert_eq!(term.cursor_row(), 0);
516
517    term.append("Test\ntext\na\nb\nc\n");
518    assert_eq!(term.cursor_row(), 5);
519    term.clear_to_color(Color::DarkBlue);
520    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521    assert_eq!(term.text_bg_color(), Color::TransparentBg);
522    assert_eq!(term.cursor_row(), 0);
523
524    // Test cursor_home()
525    term.append("Test\n\n\n\n\n\n\n\n\n\n");
526    assert_eq!(term.cursor_row(), 10);
527    term.cursor_home();
528    assert_eq!(term.cursor_row(), 0);
529
530    // Test the widget color
531    assert_eq!(term.color(), Color::Black); // Default
532    term.set_color(Color::DarkGreen);
533    assert_eq!(term.color(), Color::DarkGreen);
534    term.set_color(Color::Black);
535    assert_eq!(term.color(), Color::Black);
536    term.append(
537        "This should be one line of white text on black, embedded into the top of a blue field.\n",
538    );
539
540    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541    term.set_output_translate(OutFlags::OFF);
542    assert_eq!(term.output_translate(), OutFlags::OFF);
543    term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547    term.append("\nDone!\n");
548    term.set_text_attrib(Attrib::Normal);
549}
550
551//--------------------------------------------------------------------------------------
552/// Another set of tests for the ring-buffer access methods
553/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555    let sel_len = term.selection_text_len();
556    let sel = term.selection_text();
557
558    term.take_focus().unwrap();
559    term.reset_terminal();
560    // Test the Utf8Char primitive
561    let uc = Utf8Char::new(b'Q');
562    let uc1 = uc.text_utf8();
563    assert_eq!(&uc1, b"Q");
564    assert_eq!(&uc.attrib(), &Attrib::Normal);
565    assert_eq!(
566        &uc.charflags(),
567        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568    );
569    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570    assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572    let ring_rows = term.ring_rows();
573
574    term.take_focus().unwrap();
575    term.clear_history();
576    assert_eq!(term.history_use(), 0);
577
578    // Subtract row numbers, modulo `rows`
579    fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580        match a - b {
581            n if n < 0 => n + rows,
582            n => n,
583        }
584    }
585    // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587    assert!(term.disp_srow() >= 0);
588    assert!(term.disp_erow() >= 0);
589    assert!(term.hist_srow() >= 0);
590    assert!(term.hist_erow() >= 0);
591    assert!(term.offset() >= 0);
592    assert!(term.disp_srow() <= ring_rows);
593    assert!(term.disp_erow() <= ring_rows);
594    assert!(term.hist_srow() <= ring_rows);
595    assert!(term.hist_erow() <= ring_rows);
596    assert!(term.offset() <= ring_rows);
597
598    assert_eq!(term.ring_srow(), 0);
599    assert_eq!(term.ring_erow(), ring_rows - 1);
600    assert_eq!(
601        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602        term.display_rows()
603    );
604    assert_eq!(
605        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606        term.history_rows()
607    );
608
609    assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610    assert_eq!(term.ring_srow(), 0);
611
612    /// Local function to read back all rows from the display into a long string.
613    /// Does not include scrollback history.
614    /// Trims trailing blanks on each line
615    fn read_disp(term: &Terminal) -> String {
616        let rows = term.display_rows();
617        let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618        for row in 0..rows {
619            let r = term.u8c_disp_row(row).trim();
620            // Iterate through a row, accumulating [u8]
621            for c in r.iter() {
622                // Note: Sometimes utf-8 length is > 1
623                text.extend_from_slice(c.text_utf8());
624            }
625            text.extend_from_slice(b"\n");
626        }
627        // Return the result as a string
628        std::str::from_utf8(&text).unwrap().to_string()
629    }
630
631    term.clear();
632    term.append("Top line  ↑ (up-arrow)");
633    term.set_text_attrib(Attrib::Underline);
634    term.append("  ");
635    term.set_text_attrib(Attrib::Normal);
636    term.append("  \n");
637    let mut text_out = read_disp(term);
638    // Trim trailing empty lines
639    text_out = text_out.trim_end_matches(&"\n").to_string();
640    // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642    assert_eq!(text_out, "Top line  ↑ (up-arrow)  ");
643    let r = term.u8c_disp_row(0);
644    assert_eq!(r.col(0).text_utf8(), b"T");
645    assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646    assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647    let r = term.u8c_disp_row(1);
648    assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649    assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651    // Clear the screen again, then append test text, then read it back and compare
652    let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656            Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659    term.clear_history();
660    term.clear();
661    let bg_save = term.text_bg_color();
662    let fg_save = term.text_fg_color();
663    term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664    term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665    term.append(test_text);
666    term.set_text_bg_color(bg_save);
667    term.set_text_fg_color(fg_save);
668
669    let mut text_out = read_disp(term);
670    // Trim trailing empty lines
671    text_out = text_out.trim_end_matches(&"\n").to_string();
672    assert_eq!(test_text, text_out);
673
674    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676    assert_eq!(term.ring_srow(), 0);
677    assert_eq!(term.ring_erow(), ring_rows - 1);
678    assert_eq!(
679        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680        term.display_rows()
681    );
682    assert_eq!(
683        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684        term.history_rows()
685    );
686
687    term.append(&format!(
688        "\n\nScreen has {} rows of {} columns.\n",
689        term.display_rows(),
690        term.display_columns()
691    ));
692
693    term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
695
696//--------------------------------------------------------------------------------------
697/// Yet another set of tests for misc cursor functions and other stuff
698/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700    term.take_focus().unwrap();
701
702    // Test the attr_fg_color and attr_bg_color methods.
703    // Put a single character 'A' into the buffer and check it
704    term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705    term.set_text_bg_color(Color::TransparentBg);
706    term.set_text_fg_color(Color::XtermWhite);
707    term.append("A");
708    let r = &term.u8c_disp_row(0);
709    let uc = r.col(0);
710    assert_eq!(uc.text_utf8(), b"A");
711    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712    assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713    assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714    assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715    assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717    // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718    term.clear();
719    term.set_text_fg_color_xterm(XtermColor::White);
720    term.set_text_bg_color_xterm(XtermColor::Black);
721    assert_eq!(term.text_attrib(), Attrib::Normal);
722
723    assert!(term.ansi());
724    term.append("B\x1b[32mC\x1b[1mD\n");
725
726    let r = &term.u8c_disp_row(0);
727    let uc = r.col(0);
728    assert_eq!(uc.text_utf8(), b"B");
729    assert!(uc.is_char(b'B'));
730    assert!(!uc.is_char(b'A'));
731    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735    assert_eq!(
736        &uc.charflags(),
737        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738    );
739
740    let uc = r.col(1);
741    assert_eq!(uc.text_utf8(), b"C");
742    assert!(uc.is_char(b'C'));
743    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747    assert_eq!(
748        &uc.charflags(),
749        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750    );
751
752    let uc = r.col(2);
753    assert_eq!(uc.text_utf8(), b"D");
754    assert!(uc.is_char(b'D'));
755    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757    assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759    assert_eq!(
760        &uc.attr_fgcolor(Some(term)),
761        &Color::from_rgb(0x20, 0xf0, 0x20)
762    );
763    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764    assert_eq!(
765        &uc.charflags(),
766        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767    );
768
769    // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770    term.clear();
771    term.set_text_fg_color_xterm(XtermColor::White);
772    term.set_text_bg_color_xterm(XtermColor::Black);
773    term.set_text_attrib(Attrib::Normal);
774    assert_eq!(term.text_attrib(), Attrib::Normal);
775
776    assert!(term.ansi());
777    term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779    let r = &term.u8c_disp_row(0);
780    let uc = r.col(0);
781    assert_eq!(uc.text_utf8(), b"B");
782    assert!(uc.is_char(b'B'));
783    assert!(!uc.is_char(b'A'));
784    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788    assert_eq!(
789        &uc.charflags(),
790        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791    );
792
793    let uc = r.col(1);
794    assert_eq!(uc.text_utf8(), b"C");
795    assert!(uc.is_char(b'C'));
796    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800    assert_eq!(
801        &uc.charflags(),
802        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803    );
804
805    let uc = r.col(2);
806    assert_eq!(uc.text_utf8(), b"D");
807    assert!(uc.is_char(b'D'));
808    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812    assert_eq!(
813        &uc.charflags(),
814        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815    );
816
817    let uc = r.col(3);
818    assert_eq!(uc.text_utf8(), b"E");
819    assert!(uc.is_char(b'E'));
820    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822    assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823    assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824    assert_eq!(
825        &uc.charflags(),
826        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827    );
828
829    // Test some miscellaneous Utf8 constants
830    assert_eq!(uc.length(), 1);
831    assert_eq!(uc.max_utf8(), 4);
832    assert_eq!(uc.pwidth(), 9.0);
833    assert_eq!(uc.pwidth_int(), 9);
834
835    term.set_text_fg_color_xterm(XtermColor::White);
836    term.set_text_bg_color_xterm(XtermColor::Black);
837    term.clear();
838    term.set_text_attrib(Attrib::Normal);
839
840    // Mouse selection functions
841    term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842    term.clear_mouse_selection();
843    assert_eq!(term.get_selection(), None);
844
845    // Play with cursor position
846    term.append("0123456789\n"); // Set up test pattern
847    term.append("ABCDEFGHIJ\n");
848    term.append("abcdefghij\n");
849
850    term.set_cursor_row(1);
851    assert_eq!(term.cursor_row(), 1);
852    term.set_cursor_col(1);
853    assert_eq!(term.cursor_col(), 1);
854    assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856    term.append("----"); // Overwrites text at cursor and moves cursor forward
857    assert_eq!(term.cursor_row(), 1);
858    assert_eq!(term.cursor_col(), 5);
859    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860    term.set_cursor_col(1);
861    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863    term.cursor_up(1, false);
864    assert_eq!(term.cursor_row(), 0);
865    assert_eq!(term.cursor_col(), 1);
866    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868    // Hit top of screen, so nothing happens
869    term.cursor_up(1, false);
870    assert_eq!(term.cursor_row(), 0);
871    assert_eq!(term.cursor_col(), 1);
872    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874    // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875    term.cursor_up(1, true);
876    assert_eq!(term.cursor_row(), 0);
877    assert_eq!(term.cursor_col(), 1);
878    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880    // Go back down to the overwritten text
881    term.cursor_down(2, false);
882    assert_eq!(term.cursor_row(), 2);
883    assert_eq!(term.cursor_col(), 1);
884    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886    // Go right past the overwritten text
887    term.cursor_right(4, false);
888    assert_eq!(term.cursor_row(), 2);
889    assert_eq!(term.cursor_col(), 5);
890    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892    // Go left to the end of the overwritten text
893    term.cursor_left(1);
894    assert_eq!(term.cursor_row(), 2);
895    assert_eq!(term.cursor_col(), 4);
896    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898    // Scroll back down, removing the blank line at the top.
899    // Cursor stays in place, the text moves under it.
900    term.scroll(1);
901    assert_eq!(term.cursor_row(), 2);
902    assert_eq!(term.cursor_col(), 4);
903    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905    // Clear from here to end-of-line
906    term.clear_eol();
907    assert_eq!(term.cursor_row(), 2);
908    assert_eq!(term.cursor_col(), 4);
909    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911    // Now clear from here to start-of-line. Cursor does not move.
912    term.clear_sol();
913    assert_eq!(term.cursor_row(), 2);
914    assert_eq!(term.cursor_col(), 4);
915    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916    term.cursor_left(1);
917    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918    term.set_cursor_col(0);
919    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921    // Clear some lines
922    term.clear_line(1);
923    assert_eq!(term.cursor_row(), 2);
924    assert_eq!(term.cursor_col(), 0);
925    term.set_cursor_row(1);
926    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927    term.set_cursor_row(3);
928    term.clear_cur_line();
929    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930    assert_eq!(term.cursor_row(), 3);
931    assert_eq!(term.cursor_col(), 0);
932
933    term.append("Two lines above are intentionally left blank.\n");
934    assert_eq!(term.cursor_row(), 4);
935    assert_eq!(term.cursor_col(), 0);
936
937    // Set up the test pattern again, then play with insert/delete
938    term.append("0123456789\n");
939    term.append("ABCDEFGHIJ\n");
940    term.append("abcdefghij\n");
941    assert_eq!(term.cursor_row(), 7);
942
943    term.set_cursor_row(4);
944    term.set_cursor_col(4);
945    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947    term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949    term.cursor_right(5, false);
950    assert_eq!(term.cursor_col(), 9);
951    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953    // Insert two blank rows above cursor. Cursor stays put.
954    term.insert_rows(2);
955    assert_eq!(term.cursor_row(), 4);
956    assert_eq!(term.cursor_col(), 9);
957    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958    term.cursor_down(2, false); // Go down to find our text again
959    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961    // Go back to the beginning of the inserted 'x' characters and delete them.
962    term.cursor_left(5);
963    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964    term.delete_cur_chars(5);
965    assert_eq!(term.cursor_row(), 6);
966    assert_eq!(term.cursor_col(), 4);
967    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969    term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970    term.cursor_down(1, false);
971    term.cursor_left(2);
972    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974    term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975    assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976    term.cursor_up(1, false);
977    term.delete_rows(2); // Delete remains of test pattern
978
979    term.set_text_attrib(Attrib::Bold);
980    term.insert_char_eol('-', 3, 15, 20);
981    term.set_cursor_row(3);
982    term.set_cursor_col(15);
983    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984    assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986    term.set_text_attrib(Attrib::Italic);
987    term.append(" and all lines below");
988    term.set_text_attrib(Attrib::Normal);
989    term.cursor_down(1, false);
990
991    let mut hsb = term.hscrollbar();
992    let mut sb = term.scrollbar();
993    hsb.set_value(100.0);
994    assert_eq!(hsb.value(), 100.0);
995    sb.set_value(50.0);
996    assert_eq!(sb.value(), 50.0);
997    hsb.set_value(0.0);
998    assert_eq!(hsb.value(), 0.0);
999    sb.set_value(0.0);
1000    assert_eq!(sb.value(), 0.0);
1001}
Source

pub fn text_bg_color(&self) -> Color

Get the text background color.

Examples found in repository?
examples/terminal.rs (line 327)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
425//--------------------------------------------------------------------------------------
426/// More tests that run when the menu bar Test1 is clicked
427fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
428    term.take_focus().unwrap();
429    term.reset_terminal();
430    term.append("0123456789 0\n");
431    term.append("0123456789 1\n");
432    term.append("0123456789 2\n");
433    term.append("0123456789 3\n");
434    term.append("0123456789 4\n");
435    term.append("0123456789 5\n");
436    term.append("0123456789 6\n");
437    term.append("0123456789 7\n");
438    term.append("0123456789 8\n");
439    term.append("0123456789 9\n");
440    term.append("------------\n");
441
442    term.set_text_fg_color(Color::Green);
443    term.plot_char('A', 0, 0);
444    term.plot_char('B', 1, 1);
445    term.plot_char('C', 2, 2);
446    term.plot_char('D', 3, 3);
447    term.plot_char('E', 4, 4);
448    term.plot_char('F', 5, 5);
449    term.set_text_fg_color(Color::XtermWhite);
450
451    assert_eq!(term.cursor_row(), 11);
452    assert_eq!(term.cursor_col(), 0);
453
454    term.set_text_bg_color(Color::DarkBlue);
455    term.plot_char_utf8('b', 8, 1);
456    term.plot_char_utf8('↑', 9, 1);
457    term.plot_char_utf8('c', 8, 2);
458    term.plot_char_utf8('↑', 9, 2);
459    term.plot_char_utf8('d', 8, 3);
460    term.plot_char_utf8('↑', 9, 3);
461    term.plot_char_utf8('e', 8, 4);
462    term.plot_char_utf8('↑', 9, 4);
463    term.plot_char_utf8('f', 8, 5);
464    term.plot_char_utf8('↑', 9, 5);
465    term.plot_char_utf8('g', 8, 6);
466    term.plot_char_utf8('↑', 9, 6);
467    term.set_text_bg_color(Color::TransparentBg);
468
469    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
470    term.append("Done!\n");
471    term.set_text_attrib(Attrib::Normal);
472}
473
474//--------------------------------------------------------------------------------------
475/// More tests that run when the menu bar button Test2 is clicked
476fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
477    term.take_focus().unwrap();
478    term.reset_terminal();
479
480    for i in 0..50 {
481        term.append(&format!("{i}\n"));
482    }
483    assert_eq!(term.history_rows(), 100);
484
485    term.clear_history();
486    assert_eq!(term.history_use(), 0);
487
488    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
489    term.append("\nDone!\n");
490    term.set_text_attrib(Attrib::Normal);
491}
492
493//--------------------------------------------------------------------------------------
494/// Another set of tests that run when Test3 is clicked
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496    term.take_focus().unwrap();
497    term.reset_terminal();
498    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500    assert_eq!(term.history_use(), 0);
501    term.clear();
502    assert_eq!(term.cursor_row(), 0);
503    assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505    term.append("Test\ntext\na\nb\nc\nd");
506    assert_eq!(term.cursor_row(), 5);
507    let hist = term.history_use();
508    term.clear_screen_home(false);
509    assert_eq!(term.cursor_row(), 0);
510    assert_eq!(term.history_use(), hist); // History not changed
511
512    term.append("Test\ntext\na\nb\nc\nd\ne");
513    assert_eq!(term.cursor_row(), 6);
514    term.clear_screen_home(true);
515    assert_eq!(term.cursor_row(), 0);
516
517    term.append("Test\ntext\na\nb\nc\n");
518    assert_eq!(term.cursor_row(), 5);
519    term.clear_to_color(Color::DarkBlue);
520    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521    assert_eq!(term.text_bg_color(), Color::TransparentBg);
522    assert_eq!(term.cursor_row(), 0);
523
524    // Test cursor_home()
525    term.append("Test\n\n\n\n\n\n\n\n\n\n");
526    assert_eq!(term.cursor_row(), 10);
527    term.cursor_home();
528    assert_eq!(term.cursor_row(), 0);
529
530    // Test the widget color
531    assert_eq!(term.color(), Color::Black); // Default
532    term.set_color(Color::DarkGreen);
533    assert_eq!(term.color(), Color::DarkGreen);
534    term.set_color(Color::Black);
535    assert_eq!(term.color(), Color::Black);
536    term.append(
537        "This should be one line of white text on black, embedded into the top of a blue field.\n",
538    );
539
540    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541    term.set_output_translate(OutFlags::OFF);
542    assert_eq!(term.output_translate(), OutFlags::OFF);
543    term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547    term.append("\nDone!\n");
548    term.set_text_attrib(Attrib::Normal);
549}
550
551//--------------------------------------------------------------------------------------
552/// Another set of tests for the ring-buffer access methods
553/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555    let sel_len = term.selection_text_len();
556    let sel = term.selection_text();
557
558    term.take_focus().unwrap();
559    term.reset_terminal();
560    // Test the Utf8Char primitive
561    let uc = Utf8Char::new(b'Q');
562    let uc1 = uc.text_utf8();
563    assert_eq!(&uc1, b"Q");
564    assert_eq!(&uc.attrib(), &Attrib::Normal);
565    assert_eq!(
566        &uc.charflags(),
567        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568    );
569    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570    assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572    let ring_rows = term.ring_rows();
573
574    term.take_focus().unwrap();
575    term.clear_history();
576    assert_eq!(term.history_use(), 0);
577
578    // Subtract row numbers, modulo `rows`
579    fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580        match a - b {
581            n if n < 0 => n + rows,
582            n => n,
583        }
584    }
585    // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587    assert!(term.disp_srow() >= 0);
588    assert!(term.disp_erow() >= 0);
589    assert!(term.hist_srow() >= 0);
590    assert!(term.hist_erow() >= 0);
591    assert!(term.offset() >= 0);
592    assert!(term.disp_srow() <= ring_rows);
593    assert!(term.disp_erow() <= ring_rows);
594    assert!(term.hist_srow() <= ring_rows);
595    assert!(term.hist_erow() <= ring_rows);
596    assert!(term.offset() <= ring_rows);
597
598    assert_eq!(term.ring_srow(), 0);
599    assert_eq!(term.ring_erow(), ring_rows - 1);
600    assert_eq!(
601        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602        term.display_rows()
603    );
604    assert_eq!(
605        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606        term.history_rows()
607    );
608
609    assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610    assert_eq!(term.ring_srow(), 0);
611
612    /// Local function to read back all rows from the display into a long string.
613    /// Does not include scrollback history.
614    /// Trims trailing blanks on each line
615    fn read_disp(term: &Terminal) -> String {
616        let rows = term.display_rows();
617        let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618        for row in 0..rows {
619            let r = term.u8c_disp_row(row).trim();
620            // Iterate through a row, accumulating [u8]
621            for c in r.iter() {
622                // Note: Sometimes utf-8 length is > 1
623                text.extend_from_slice(c.text_utf8());
624            }
625            text.extend_from_slice(b"\n");
626        }
627        // Return the result as a string
628        std::str::from_utf8(&text).unwrap().to_string()
629    }
630
631    term.clear();
632    term.append("Top line  ↑ (up-arrow)");
633    term.set_text_attrib(Attrib::Underline);
634    term.append("  ");
635    term.set_text_attrib(Attrib::Normal);
636    term.append("  \n");
637    let mut text_out = read_disp(term);
638    // Trim trailing empty lines
639    text_out = text_out.trim_end_matches(&"\n").to_string();
640    // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642    assert_eq!(text_out, "Top line  ↑ (up-arrow)  ");
643    let r = term.u8c_disp_row(0);
644    assert_eq!(r.col(0).text_utf8(), b"T");
645    assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646    assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647    let r = term.u8c_disp_row(1);
648    assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649    assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651    // Clear the screen again, then append test text, then read it back and compare
652    let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656            Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659    term.clear_history();
660    term.clear();
661    let bg_save = term.text_bg_color();
662    let fg_save = term.text_fg_color();
663    term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664    term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665    term.append(test_text);
666    term.set_text_bg_color(bg_save);
667    term.set_text_fg_color(fg_save);
668
669    let mut text_out = read_disp(term);
670    // Trim trailing empty lines
671    text_out = text_out.trim_end_matches(&"\n").to_string();
672    assert_eq!(test_text, text_out);
673
674    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676    assert_eq!(term.ring_srow(), 0);
677    assert_eq!(term.ring_erow(), ring_rows - 1);
678    assert_eq!(
679        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680        term.display_rows()
681    );
682    assert_eq!(
683        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684        term.history_rows()
685    );
686
687    term.append(&format!(
688        "\n\nScreen has {} rows of {} columns.\n",
689        term.display_rows(),
690        term.display_columns()
691    ));
692
693    term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
Source

pub fn set_text_bg_color_default(&mut self, color: Color)

Set the default text background color used by <ESC>c, <ESC>[0m, and reset_terminal(). Does not affect the ‘current’ text fg color; use set_text_bg_color(Fl_Color) to set that.

Examples found in repository?
examples/terminal.rs (line 318)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn text_bg_color_default(&self) -> Color

Return the default text background color.

Examples found in repository?
examples/terminal.rs (line 313)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
425//--------------------------------------------------------------------------------------
426/// More tests that run when the menu bar Test1 is clicked
427fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
428    term.take_focus().unwrap();
429    term.reset_terminal();
430    term.append("0123456789 0\n");
431    term.append("0123456789 1\n");
432    term.append("0123456789 2\n");
433    term.append("0123456789 3\n");
434    term.append("0123456789 4\n");
435    term.append("0123456789 5\n");
436    term.append("0123456789 6\n");
437    term.append("0123456789 7\n");
438    term.append("0123456789 8\n");
439    term.append("0123456789 9\n");
440    term.append("------------\n");
441
442    term.set_text_fg_color(Color::Green);
443    term.plot_char('A', 0, 0);
444    term.plot_char('B', 1, 1);
445    term.plot_char('C', 2, 2);
446    term.plot_char('D', 3, 3);
447    term.plot_char('E', 4, 4);
448    term.plot_char('F', 5, 5);
449    term.set_text_fg_color(Color::XtermWhite);
450
451    assert_eq!(term.cursor_row(), 11);
452    assert_eq!(term.cursor_col(), 0);
453
454    term.set_text_bg_color(Color::DarkBlue);
455    term.plot_char_utf8('b', 8, 1);
456    term.plot_char_utf8('↑', 9, 1);
457    term.plot_char_utf8('c', 8, 2);
458    term.plot_char_utf8('↑', 9, 2);
459    term.plot_char_utf8('d', 8, 3);
460    term.plot_char_utf8('↑', 9, 3);
461    term.plot_char_utf8('e', 8, 4);
462    term.plot_char_utf8('↑', 9, 4);
463    term.plot_char_utf8('f', 8, 5);
464    term.plot_char_utf8('↑', 9, 5);
465    term.plot_char_utf8('g', 8, 6);
466    term.plot_char_utf8('↑', 9, 6);
467    term.set_text_bg_color(Color::TransparentBg);
468
469    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
470    term.append("Done!\n");
471    term.set_text_attrib(Attrib::Normal);
472}
473
474//--------------------------------------------------------------------------------------
475/// More tests that run when the menu bar button Test2 is clicked
476fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
477    term.take_focus().unwrap();
478    term.reset_terminal();
479
480    for i in 0..50 {
481        term.append(&format!("{i}\n"));
482    }
483    assert_eq!(term.history_rows(), 100);
484
485    term.clear_history();
486    assert_eq!(term.history_use(), 0);
487
488    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
489    term.append("\nDone!\n");
490    term.set_text_attrib(Attrib::Normal);
491}
492
493//--------------------------------------------------------------------------------------
494/// Another set of tests that run when Test3 is clicked
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496    term.take_focus().unwrap();
497    term.reset_terminal();
498    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500    assert_eq!(term.history_use(), 0);
501    term.clear();
502    assert_eq!(term.cursor_row(), 0);
503    assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505    term.append("Test\ntext\na\nb\nc\nd");
506    assert_eq!(term.cursor_row(), 5);
507    let hist = term.history_use();
508    term.clear_screen_home(false);
509    assert_eq!(term.cursor_row(), 0);
510    assert_eq!(term.history_use(), hist); // History not changed
511
512    term.append("Test\ntext\na\nb\nc\nd\ne");
513    assert_eq!(term.cursor_row(), 6);
514    term.clear_screen_home(true);
515    assert_eq!(term.cursor_row(), 0);
516
517    term.append("Test\ntext\na\nb\nc\n");
518    assert_eq!(term.cursor_row(), 5);
519    term.clear_to_color(Color::DarkBlue);
520    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521    assert_eq!(term.text_bg_color(), Color::TransparentBg);
522    assert_eq!(term.cursor_row(), 0);
523
524    // Test cursor_home()
525    term.append("Test\n\n\n\n\n\n\n\n\n\n");
526    assert_eq!(term.cursor_row(), 10);
527    term.cursor_home();
528    assert_eq!(term.cursor_row(), 0);
529
530    // Test the widget color
531    assert_eq!(term.color(), Color::Black); // Default
532    term.set_color(Color::DarkGreen);
533    assert_eq!(term.color(), Color::DarkGreen);
534    term.set_color(Color::Black);
535    assert_eq!(term.color(), Color::Black);
536    term.append(
537        "This should be one line of white text on black, embedded into the top of a blue field.\n",
538    );
539
540    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541    term.set_output_translate(OutFlags::OFF);
542    assert_eq!(term.output_translate(), OutFlags::OFF);
543    term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547    term.append("\nDone!\n");
548    term.set_text_attrib(Attrib::Normal);
549}
Source

pub fn set_text_bg_color_xterm(&mut self, color: XtermColor)

Sets the background text color as one of the 8 ‘xterm color’ values.

This will be the background color used for all newly printed text, similar to the <ESC>[#m escape sequence, where # is between 40 and 47.

This color will be reset to the default bg color if reset_terminal() is called, or by <ESC>c, <ESC>[0m, etc.

The xterm color intensity values can be influenced by the Dim/Bold/Normal modes (which can be set with e.g. <ESC>[1m, textattrib(), etc), so the actual RGB values of these colors allow room for Dim/Bold to influence their brightness. For instance, “Normal Red” is not full brightness to allow “Bold Red” to be brighter. This goes for all colors except ‘Black’, which is not influenced by Dim or Bold; Black is always Black.

These background colors are slightly dimmer than the corresponding xterm foregroumd colors.

The 8 color xterm values are: 0 = Black, 1 = Red, 2 = Green, 3 = Yellow, 4 = Blue,5 = Magenta, 6 = Cyan, 7 = White

Examples found in repository?
examples/terminal.rs (line 720)
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700    term.take_focus().unwrap();
701
702    // Test the attr_fg_color and attr_bg_color methods.
703    // Put a single character 'A' into the buffer and check it
704    term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705    term.set_text_bg_color(Color::TransparentBg);
706    term.set_text_fg_color(Color::XtermWhite);
707    term.append("A");
708    let r = &term.u8c_disp_row(0);
709    let uc = r.col(0);
710    assert_eq!(uc.text_utf8(), b"A");
711    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712    assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713    assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714    assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715    assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717    // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718    term.clear();
719    term.set_text_fg_color_xterm(XtermColor::White);
720    term.set_text_bg_color_xterm(XtermColor::Black);
721    assert_eq!(term.text_attrib(), Attrib::Normal);
722
723    assert!(term.ansi());
724    term.append("B\x1b[32mC\x1b[1mD\n");
725
726    let r = &term.u8c_disp_row(0);
727    let uc = r.col(0);
728    assert_eq!(uc.text_utf8(), b"B");
729    assert!(uc.is_char(b'B'));
730    assert!(!uc.is_char(b'A'));
731    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735    assert_eq!(
736        &uc.charflags(),
737        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738    );
739
740    let uc = r.col(1);
741    assert_eq!(uc.text_utf8(), b"C");
742    assert!(uc.is_char(b'C'));
743    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747    assert_eq!(
748        &uc.charflags(),
749        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750    );
751
752    let uc = r.col(2);
753    assert_eq!(uc.text_utf8(), b"D");
754    assert!(uc.is_char(b'D'));
755    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757    assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759    assert_eq!(
760        &uc.attr_fgcolor(Some(term)),
761        &Color::from_rgb(0x20, 0xf0, 0x20)
762    );
763    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764    assert_eq!(
765        &uc.charflags(),
766        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767    );
768
769    // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770    term.clear();
771    term.set_text_fg_color_xterm(XtermColor::White);
772    term.set_text_bg_color_xterm(XtermColor::Black);
773    term.set_text_attrib(Attrib::Normal);
774    assert_eq!(term.text_attrib(), Attrib::Normal);
775
776    assert!(term.ansi());
777    term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779    let r = &term.u8c_disp_row(0);
780    let uc = r.col(0);
781    assert_eq!(uc.text_utf8(), b"B");
782    assert!(uc.is_char(b'B'));
783    assert!(!uc.is_char(b'A'));
784    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788    assert_eq!(
789        &uc.charflags(),
790        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791    );
792
793    let uc = r.col(1);
794    assert_eq!(uc.text_utf8(), b"C");
795    assert!(uc.is_char(b'C'));
796    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800    assert_eq!(
801        &uc.charflags(),
802        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803    );
804
805    let uc = r.col(2);
806    assert_eq!(uc.text_utf8(), b"D");
807    assert!(uc.is_char(b'D'));
808    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812    assert_eq!(
813        &uc.charflags(),
814        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815    );
816
817    let uc = r.col(3);
818    assert_eq!(uc.text_utf8(), b"E");
819    assert!(uc.is_char(b'E'));
820    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822    assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823    assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824    assert_eq!(
825        &uc.charflags(),
826        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827    );
828
829    // Test some miscellaneous Utf8 constants
830    assert_eq!(uc.length(), 1);
831    assert_eq!(uc.max_utf8(), 4);
832    assert_eq!(uc.pwidth(), 9.0);
833    assert_eq!(uc.pwidth_int(), 9);
834
835    term.set_text_fg_color_xterm(XtermColor::White);
836    term.set_text_bg_color_xterm(XtermColor::Black);
837    term.clear();
838    term.set_text_attrib(Attrib::Normal);
839
840    // Mouse selection functions
841    term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842    term.clear_mouse_selection();
843    assert_eq!(term.get_selection(), None);
844
845    // Play with cursor position
846    term.append("0123456789\n"); // Set up test pattern
847    term.append("ABCDEFGHIJ\n");
848    term.append("abcdefghij\n");
849
850    term.set_cursor_row(1);
851    assert_eq!(term.cursor_row(), 1);
852    term.set_cursor_col(1);
853    assert_eq!(term.cursor_col(), 1);
854    assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856    term.append("----"); // Overwrites text at cursor and moves cursor forward
857    assert_eq!(term.cursor_row(), 1);
858    assert_eq!(term.cursor_col(), 5);
859    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860    term.set_cursor_col(1);
861    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863    term.cursor_up(1, false);
864    assert_eq!(term.cursor_row(), 0);
865    assert_eq!(term.cursor_col(), 1);
866    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868    // Hit top of screen, so nothing happens
869    term.cursor_up(1, false);
870    assert_eq!(term.cursor_row(), 0);
871    assert_eq!(term.cursor_col(), 1);
872    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874    // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875    term.cursor_up(1, true);
876    assert_eq!(term.cursor_row(), 0);
877    assert_eq!(term.cursor_col(), 1);
878    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880    // Go back down to the overwritten text
881    term.cursor_down(2, false);
882    assert_eq!(term.cursor_row(), 2);
883    assert_eq!(term.cursor_col(), 1);
884    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886    // Go right past the overwritten text
887    term.cursor_right(4, false);
888    assert_eq!(term.cursor_row(), 2);
889    assert_eq!(term.cursor_col(), 5);
890    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892    // Go left to the end of the overwritten text
893    term.cursor_left(1);
894    assert_eq!(term.cursor_row(), 2);
895    assert_eq!(term.cursor_col(), 4);
896    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898    // Scroll back down, removing the blank line at the top.
899    // Cursor stays in place, the text moves under it.
900    term.scroll(1);
901    assert_eq!(term.cursor_row(), 2);
902    assert_eq!(term.cursor_col(), 4);
903    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905    // Clear from here to end-of-line
906    term.clear_eol();
907    assert_eq!(term.cursor_row(), 2);
908    assert_eq!(term.cursor_col(), 4);
909    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911    // Now clear from here to start-of-line. Cursor does not move.
912    term.clear_sol();
913    assert_eq!(term.cursor_row(), 2);
914    assert_eq!(term.cursor_col(), 4);
915    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916    term.cursor_left(1);
917    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918    term.set_cursor_col(0);
919    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921    // Clear some lines
922    term.clear_line(1);
923    assert_eq!(term.cursor_row(), 2);
924    assert_eq!(term.cursor_col(), 0);
925    term.set_cursor_row(1);
926    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927    term.set_cursor_row(3);
928    term.clear_cur_line();
929    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930    assert_eq!(term.cursor_row(), 3);
931    assert_eq!(term.cursor_col(), 0);
932
933    term.append("Two lines above are intentionally left blank.\n");
934    assert_eq!(term.cursor_row(), 4);
935    assert_eq!(term.cursor_col(), 0);
936
937    // Set up the test pattern again, then play with insert/delete
938    term.append("0123456789\n");
939    term.append("ABCDEFGHIJ\n");
940    term.append("abcdefghij\n");
941    assert_eq!(term.cursor_row(), 7);
942
943    term.set_cursor_row(4);
944    term.set_cursor_col(4);
945    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947    term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949    term.cursor_right(5, false);
950    assert_eq!(term.cursor_col(), 9);
951    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953    // Insert two blank rows above cursor. Cursor stays put.
954    term.insert_rows(2);
955    assert_eq!(term.cursor_row(), 4);
956    assert_eq!(term.cursor_col(), 9);
957    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958    term.cursor_down(2, false); // Go down to find our text again
959    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961    // Go back to the beginning of the inserted 'x' characters and delete them.
962    term.cursor_left(5);
963    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964    term.delete_cur_chars(5);
965    assert_eq!(term.cursor_row(), 6);
966    assert_eq!(term.cursor_col(), 4);
967    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969    term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970    term.cursor_down(1, false);
971    term.cursor_left(2);
972    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974    term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975    assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976    term.cursor_up(1, false);
977    term.delete_rows(2); // Delete remains of test pattern
978
979    term.set_text_attrib(Attrib::Bold);
980    term.insert_char_eol('-', 3, 15, 20);
981    term.set_cursor_row(3);
982    term.set_cursor_col(15);
983    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984    assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986    term.set_text_attrib(Attrib::Italic);
987    term.append(" and all lines below");
988    term.set_text_attrib(Attrib::Normal);
989    term.cursor_down(1, false);
990
991    let mut hsb = term.hscrollbar();
992    let mut sb = term.scrollbar();
993    hsb.set_value(100.0);
994    assert_eq!(hsb.value(), 100.0);
995    sb.set_value(50.0);
996    assert_eq!(sb.value(), 50.0);
997    hsb.set_value(0.0);
998    assert_eq!(hsb.value(), 0.0);
999    sb.set_value(0.0);
1000    assert_eq!(sb.value(), 0.0);
1001}
Source

pub fn set_text_color(&mut self, color: Color)

Set the text color for the terminal. This is a convenience method that sets both textfgcolor() and textfgcolor_default(), ensuring both are set to the same value.

Source

pub fn set_text_fg_color(&mut self, color: Color)

Set text foreground drawing color to fltk color val. Use this for temporary color changes, similar to <ESC>[38;2;{R};{G};{B}m

This setting does not affect the ‘default’ text colors used by <ESC>[0m, <ESC>c, reset_terminal(), etc. To change both the current and default fg color, also use textfgcolor_default(Fl_Color)

Examples found in repository?
examples/terminal.rs (line 331)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
425//--------------------------------------------------------------------------------------
426/// More tests that run when the menu bar Test1 is clicked
427fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
428    term.take_focus().unwrap();
429    term.reset_terminal();
430    term.append("0123456789 0\n");
431    term.append("0123456789 1\n");
432    term.append("0123456789 2\n");
433    term.append("0123456789 3\n");
434    term.append("0123456789 4\n");
435    term.append("0123456789 5\n");
436    term.append("0123456789 6\n");
437    term.append("0123456789 7\n");
438    term.append("0123456789 8\n");
439    term.append("0123456789 9\n");
440    term.append("------------\n");
441
442    term.set_text_fg_color(Color::Green);
443    term.plot_char('A', 0, 0);
444    term.plot_char('B', 1, 1);
445    term.plot_char('C', 2, 2);
446    term.plot_char('D', 3, 3);
447    term.plot_char('E', 4, 4);
448    term.plot_char('F', 5, 5);
449    term.set_text_fg_color(Color::XtermWhite);
450
451    assert_eq!(term.cursor_row(), 11);
452    assert_eq!(term.cursor_col(), 0);
453
454    term.set_text_bg_color(Color::DarkBlue);
455    term.plot_char_utf8('b', 8, 1);
456    term.plot_char_utf8('↑', 9, 1);
457    term.plot_char_utf8('c', 8, 2);
458    term.plot_char_utf8('↑', 9, 2);
459    term.plot_char_utf8('d', 8, 3);
460    term.plot_char_utf8('↑', 9, 3);
461    term.plot_char_utf8('e', 8, 4);
462    term.plot_char_utf8('↑', 9, 4);
463    term.plot_char_utf8('f', 8, 5);
464    term.plot_char_utf8('↑', 9, 5);
465    term.plot_char_utf8('g', 8, 6);
466    term.plot_char_utf8('↑', 9, 6);
467    term.set_text_bg_color(Color::TransparentBg);
468
469    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
470    term.append("Done!\n");
471    term.set_text_attrib(Attrib::Normal);
472}
473
474//--------------------------------------------------------------------------------------
475/// More tests that run when the menu bar button Test2 is clicked
476fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
477    term.take_focus().unwrap();
478    term.reset_terminal();
479
480    for i in 0..50 {
481        term.append(&format!("{i}\n"));
482    }
483    assert_eq!(term.history_rows(), 100);
484
485    term.clear_history();
486    assert_eq!(term.history_use(), 0);
487
488    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
489    term.append("\nDone!\n");
490    term.set_text_attrib(Attrib::Normal);
491}
492
493//--------------------------------------------------------------------------------------
494/// Another set of tests that run when Test3 is clicked
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496    term.take_focus().unwrap();
497    term.reset_terminal();
498    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500    assert_eq!(term.history_use(), 0);
501    term.clear();
502    assert_eq!(term.cursor_row(), 0);
503    assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505    term.append("Test\ntext\na\nb\nc\nd");
506    assert_eq!(term.cursor_row(), 5);
507    let hist = term.history_use();
508    term.clear_screen_home(false);
509    assert_eq!(term.cursor_row(), 0);
510    assert_eq!(term.history_use(), hist); // History not changed
511
512    term.append("Test\ntext\na\nb\nc\nd\ne");
513    assert_eq!(term.cursor_row(), 6);
514    term.clear_screen_home(true);
515    assert_eq!(term.cursor_row(), 0);
516
517    term.append("Test\ntext\na\nb\nc\n");
518    assert_eq!(term.cursor_row(), 5);
519    term.clear_to_color(Color::DarkBlue);
520    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521    assert_eq!(term.text_bg_color(), Color::TransparentBg);
522    assert_eq!(term.cursor_row(), 0);
523
524    // Test cursor_home()
525    term.append("Test\n\n\n\n\n\n\n\n\n\n");
526    assert_eq!(term.cursor_row(), 10);
527    term.cursor_home();
528    assert_eq!(term.cursor_row(), 0);
529
530    // Test the widget color
531    assert_eq!(term.color(), Color::Black); // Default
532    term.set_color(Color::DarkGreen);
533    assert_eq!(term.color(), Color::DarkGreen);
534    term.set_color(Color::Black);
535    assert_eq!(term.color(), Color::Black);
536    term.append(
537        "This should be one line of white text on black, embedded into the top of a blue field.\n",
538    );
539
540    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541    term.set_output_translate(OutFlags::OFF);
542    assert_eq!(term.output_translate(), OutFlags::OFF);
543    term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547    term.append("\nDone!\n");
548    term.set_text_attrib(Attrib::Normal);
549}
550
551//--------------------------------------------------------------------------------------
552/// Another set of tests for the ring-buffer access methods
553/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555    let sel_len = term.selection_text_len();
556    let sel = term.selection_text();
557
558    term.take_focus().unwrap();
559    term.reset_terminal();
560    // Test the Utf8Char primitive
561    let uc = Utf8Char::new(b'Q');
562    let uc1 = uc.text_utf8();
563    assert_eq!(&uc1, b"Q");
564    assert_eq!(&uc.attrib(), &Attrib::Normal);
565    assert_eq!(
566        &uc.charflags(),
567        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568    );
569    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570    assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572    let ring_rows = term.ring_rows();
573
574    term.take_focus().unwrap();
575    term.clear_history();
576    assert_eq!(term.history_use(), 0);
577
578    // Subtract row numbers, modulo `rows`
579    fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580        match a - b {
581            n if n < 0 => n + rows,
582            n => n,
583        }
584    }
585    // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587    assert!(term.disp_srow() >= 0);
588    assert!(term.disp_erow() >= 0);
589    assert!(term.hist_srow() >= 0);
590    assert!(term.hist_erow() >= 0);
591    assert!(term.offset() >= 0);
592    assert!(term.disp_srow() <= ring_rows);
593    assert!(term.disp_erow() <= ring_rows);
594    assert!(term.hist_srow() <= ring_rows);
595    assert!(term.hist_erow() <= ring_rows);
596    assert!(term.offset() <= ring_rows);
597
598    assert_eq!(term.ring_srow(), 0);
599    assert_eq!(term.ring_erow(), ring_rows - 1);
600    assert_eq!(
601        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602        term.display_rows()
603    );
604    assert_eq!(
605        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606        term.history_rows()
607    );
608
609    assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610    assert_eq!(term.ring_srow(), 0);
611
612    /// Local function to read back all rows from the display into a long string.
613    /// Does not include scrollback history.
614    /// Trims trailing blanks on each line
615    fn read_disp(term: &Terminal) -> String {
616        let rows = term.display_rows();
617        let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618        for row in 0..rows {
619            let r = term.u8c_disp_row(row).trim();
620            // Iterate through a row, accumulating [u8]
621            for c in r.iter() {
622                // Note: Sometimes utf-8 length is > 1
623                text.extend_from_slice(c.text_utf8());
624            }
625            text.extend_from_slice(b"\n");
626        }
627        // Return the result as a string
628        std::str::from_utf8(&text).unwrap().to_string()
629    }
630
631    term.clear();
632    term.append("Top line  ↑ (up-arrow)");
633    term.set_text_attrib(Attrib::Underline);
634    term.append("  ");
635    term.set_text_attrib(Attrib::Normal);
636    term.append("  \n");
637    let mut text_out = read_disp(term);
638    // Trim trailing empty lines
639    text_out = text_out.trim_end_matches(&"\n").to_string();
640    // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642    assert_eq!(text_out, "Top line  ↑ (up-arrow)  ");
643    let r = term.u8c_disp_row(0);
644    assert_eq!(r.col(0).text_utf8(), b"T");
645    assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646    assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647    let r = term.u8c_disp_row(1);
648    assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649    assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651    // Clear the screen again, then append test text, then read it back and compare
652    let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656            Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659    term.clear_history();
660    term.clear();
661    let bg_save = term.text_bg_color();
662    let fg_save = term.text_fg_color();
663    term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664    term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665    term.append(test_text);
666    term.set_text_bg_color(bg_save);
667    term.set_text_fg_color(fg_save);
668
669    let mut text_out = read_disp(term);
670    // Trim trailing empty lines
671    text_out = text_out.trim_end_matches(&"\n").to_string();
672    assert_eq!(test_text, text_out);
673
674    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676    assert_eq!(term.ring_srow(), 0);
677    assert_eq!(term.ring_erow(), ring_rows - 1);
678    assert_eq!(
679        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680        term.display_rows()
681    );
682    assert_eq!(
683        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684        term.history_rows()
685    );
686
687    term.append(&format!(
688        "\n\nScreen has {} rows of {} columns.\n",
689        term.display_rows(),
690        term.display_columns()
691    ));
692
693    term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
695
696//--------------------------------------------------------------------------------------
697/// Yet another set of tests for misc cursor functions and other stuff
698/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700    term.take_focus().unwrap();
701
702    // Test the attr_fg_color and attr_bg_color methods.
703    // Put a single character 'A' into the buffer and check it
704    term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705    term.set_text_bg_color(Color::TransparentBg);
706    term.set_text_fg_color(Color::XtermWhite);
707    term.append("A");
708    let r = &term.u8c_disp_row(0);
709    let uc = r.col(0);
710    assert_eq!(uc.text_utf8(), b"A");
711    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712    assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713    assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714    assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715    assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717    // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718    term.clear();
719    term.set_text_fg_color_xterm(XtermColor::White);
720    term.set_text_bg_color_xterm(XtermColor::Black);
721    assert_eq!(term.text_attrib(), Attrib::Normal);
722
723    assert!(term.ansi());
724    term.append("B\x1b[32mC\x1b[1mD\n");
725
726    let r = &term.u8c_disp_row(0);
727    let uc = r.col(0);
728    assert_eq!(uc.text_utf8(), b"B");
729    assert!(uc.is_char(b'B'));
730    assert!(!uc.is_char(b'A'));
731    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735    assert_eq!(
736        &uc.charflags(),
737        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738    );
739
740    let uc = r.col(1);
741    assert_eq!(uc.text_utf8(), b"C");
742    assert!(uc.is_char(b'C'));
743    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747    assert_eq!(
748        &uc.charflags(),
749        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750    );
751
752    let uc = r.col(2);
753    assert_eq!(uc.text_utf8(), b"D");
754    assert!(uc.is_char(b'D'));
755    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757    assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759    assert_eq!(
760        &uc.attr_fgcolor(Some(term)),
761        &Color::from_rgb(0x20, 0xf0, 0x20)
762    );
763    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764    assert_eq!(
765        &uc.charflags(),
766        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767    );
768
769    // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770    term.clear();
771    term.set_text_fg_color_xterm(XtermColor::White);
772    term.set_text_bg_color_xterm(XtermColor::Black);
773    term.set_text_attrib(Attrib::Normal);
774    assert_eq!(term.text_attrib(), Attrib::Normal);
775
776    assert!(term.ansi());
777    term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779    let r = &term.u8c_disp_row(0);
780    let uc = r.col(0);
781    assert_eq!(uc.text_utf8(), b"B");
782    assert!(uc.is_char(b'B'));
783    assert!(!uc.is_char(b'A'));
784    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788    assert_eq!(
789        &uc.charflags(),
790        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791    );
792
793    let uc = r.col(1);
794    assert_eq!(uc.text_utf8(), b"C");
795    assert!(uc.is_char(b'C'));
796    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800    assert_eq!(
801        &uc.charflags(),
802        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803    );
804
805    let uc = r.col(2);
806    assert_eq!(uc.text_utf8(), b"D");
807    assert!(uc.is_char(b'D'));
808    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812    assert_eq!(
813        &uc.charflags(),
814        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815    );
816
817    let uc = r.col(3);
818    assert_eq!(uc.text_utf8(), b"E");
819    assert!(uc.is_char(b'E'));
820    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822    assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823    assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824    assert_eq!(
825        &uc.charflags(),
826        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827    );
828
829    // Test some miscellaneous Utf8 constants
830    assert_eq!(uc.length(), 1);
831    assert_eq!(uc.max_utf8(), 4);
832    assert_eq!(uc.pwidth(), 9.0);
833    assert_eq!(uc.pwidth_int(), 9);
834
835    term.set_text_fg_color_xterm(XtermColor::White);
836    term.set_text_bg_color_xterm(XtermColor::Black);
837    term.clear();
838    term.set_text_attrib(Attrib::Normal);
839
840    // Mouse selection functions
841    term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842    term.clear_mouse_selection();
843    assert_eq!(term.get_selection(), None);
844
845    // Play with cursor position
846    term.append("0123456789\n"); // Set up test pattern
847    term.append("ABCDEFGHIJ\n");
848    term.append("abcdefghij\n");
849
850    term.set_cursor_row(1);
851    assert_eq!(term.cursor_row(), 1);
852    term.set_cursor_col(1);
853    assert_eq!(term.cursor_col(), 1);
854    assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856    term.append("----"); // Overwrites text at cursor and moves cursor forward
857    assert_eq!(term.cursor_row(), 1);
858    assert_eq!(term.cursor_col(), 5);
859    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860    term.set_cursor_col(1);
861    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863    term.cursor_up(1, false);
864    assert_eq!(term.cursor_row(), 0);
865    assert_eq!(term.cursor_col(), 1);
866    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868    // Hit top of screen, so nothing happens
869    term.cursor_up(1, false);
870    assert_eq!(term.cursor_row(), 0);
871    assert_eq!(term.cursor_col(), 1);
872    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874    // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875    term.cursor_up(1, true);
876    assert_eq!(term.cursor_row(), 0);
877    assert_eq!(term.cursor_col(), 1);
878    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880    // Go back down to the overwritten text
881    term.cursor_down(2, false);
882    assert_eq!(term.cursor_row(), 2);
883    assert_eq!(term.cursor_col(), 1);
884    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886    // Go right past the overwritten text
887    term.cursor_right(4, false);
888    assert_eq!(term.cursor_row(), 2);
889    assert_eq!(term.cursor_col(), 5);
890    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892    // Go left to the end of the overwritten text
893    term.cursor_left(1);
894    assert_eq!(term.cursor_row(), 2);
895    assert_eq!(term.cursor_col(), 4);
896    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898    // Scroll back down, removing the blank line at the top.
899    // Cursor stays in place, the text moves under it.
900    term.scroll(1);
901    assert_eq!(term.cursor_row(), 2);
902    assert_eq!(term.cursor_col(), 4);
903    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905    // Clear from here to end-of-line
906    term.clear_eol();
907    assert_eq!(term.cursor_row(), 2);
908    assert_eq!(term.cursor_col(), 4);
909    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911    // Now clear from here to start-of-line. Cursor does not move.
912    term.clear_sol();
913    assert_eq!(term.cursor_row(), 2);
914    assert_eq!(term.cursor_col(), 4);
915    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916    term.cursor_left(1);
917    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918    term.set_cursor_col(0);
919    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921    // Clear some lines
922    term.clear_line(1);
923    assert_eq!(term.cursor_row(), 2);
924    assert_eq!(term.cursor_col(), 0);
925    term.set_cursor_row(1);
926    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927    term.set_cursor_row(3);
928    term.clear_cur_line();
929    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930    assert_eq!(term.cursor_row(), 3);
931    assert_eq!(term.cursor_col(), 0);
932
933    term.append("Two lines above are intentionally left blank.\n");
934    assert_eq!(term.cursor_row(), 4);
935    assert_eq!(term.cursor_col(), 0);
936
937    // Set up the test pattern again, then play with insert/delete
938    term.append("0123456789\n");
939    term.append("ABCDEFGHIJ\n");
940    term.append("abcdefghij\n");
941    assert_eq!(term.cursor_row(), 7);
942
943    term.set_cursor_row(4);
944    term.set_cursor_col(4);
945    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947    term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949    term.cursor_right(5, false);
950    assert_eq!(term.cursor_col(), 9);
951    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953    // Insert two blank rows above cursor. Cursor stays put.
954    term.insert_rows(2);
955    assert_eq!(term.cursor_row(), 4);
956    assert_eq!(term.cursor_col(), 9);
957    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958    term.cursor_down(2, false); // Go down to find our text again
959    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961    // Go back to the beginning of the inserted 'x' characters and delete them.
962    term.cursor_left(5);
963    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964    term.delete_cur_chars(5);
965    assert_eq!(term.cursor_row(), 6);
966    assert_eq!(term.cursor_col(), 4);
967    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969    term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970    term.cursor_down(1, false);
971    term.cursor_left(2);
972    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974    term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975    assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976    term.cursor_up(1, false);
977    term.delete_rows(2); // Delete remains of test pattern
978
979    term.set_text_attrib(Attrib::Bold);
980    term.insert_char_eol('-', 3, 15, 20);
981    term.set_cursor_row(3);
982    term.set_cursor_col(15);
983    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984    assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986    term.set_text_attrib(Attrib::Italic);
987    term.append(" and all lines below");
988    term.set_text_attrib(Attrib::Normal);
989    term.cursor_down(1, false);
990
991    let mut hsb = term.hscrollbar();
992    let mut sb = term.scrollbar();
993    hsb.set_value(100.0);
994    assert_eq!(hsb.value(), 100.0);
995    sb.set_value(50.0);
996    assert_eq!(sb.value(), 50.0);
997    hsb.set_value(0.0);
998    assert_eq!(hsb.value(), 0.0);
999    sb.set_value(0.0);
1000    assert_eq!(sb.value(), 0.0);
1001}
Source

pub fn text_fg_color(&self) -> Color

Get the text foreground color.

Examples found in repository?
examples/terminal.rs (line 326)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
425//--------------------------------------------------------------------------------------
426/// More tests that run when the menu bar Test1 is clicked
427fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
428    term.take_focus().unwrap();
429    term.reset_terminal();
430    term.append("0123456789 0\n");
431    term.append("0123456789 1\n");
432    term.append("0123456789 2\n");
433    term.append("0123456789 3\n");
434    term.append("0123456789 4\n");
435    term.append("0123456789 5\n");
436    term.append("0123456789 6\n");
437    term.append("0123456789 7\n");
438    term.append("0123456789 8\n");
439    term.append("0123456789 9\n");
440    term.append("------------\n");
441
442    term.set_text_fg_color(Color::Green);
443    term.plot_char('A', 0, 0);
444    term.plot_char('B', 1, 1);
445    term.plot_char('C', 2, 2);
446    term.plot_char('D', 3, 3);
447    term.plot_char('E', 4, 4);
448    term.plot_char('F', 5, 5);
449    term.set_text_fg_color(Color::XtermWhite);
450
451    assert_eq!(term.cursor_row(), 11);
452    assert_eq!(term.cursor_col(), 0);
453
454    term.set_text_bg_color(Color::DarkBlue);
455    term.plot_char_utf8('b', 8, 1);
456    term.plot_char_utf8('↑', 9, 1);
457    term.plot_char_utf8('c', 8, 2);
458    term.plot_char_utf8('↑', 9, 2);
459    term.plot_char_utf8('d', 8, 3);
460    term.plot_char_utf8('↑', 9, 3);
461    term.plot_char_utf8('e', 8, 4);
462    term.plot_char_utf8('↑', 9, 4);
463    term.plot_char_utf8('f', 8, 5);
464    term.plot_char_utf8('↑', 9, 5);
465    term.plot_char_utf8('g', 8, 6);
466    term.plot_char_utf8('↑', 9, 6);
467    term.set_text_bg_color(Color::TransparentBg);
468
469    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
470    term.append("Done!\n");
471    term.set_text_attrib(Attrib::Normal);
472}
473
474//--------------------------------------------------------------------------------------
475/// More tests that run when the menu bar button Test2 is clicked
476fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
477    term.take_focus().unwrap();
478    term.reset_terminal();
479
480    for i in 0..50 {
481        term.append(&format!("{i}\n"));
482    }
483    assert_eq!(term.history_rows(), 100);
484
485    term.clear_history();
486    assert_eq!(term.history_use(), 0);
487
488    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
489    term.append("\nDone!\n");
490    term.set_text_attrib(Attrib::Normal);
491}
492
493//--------------------------------------------------------------------------------------
494/// Another set of tests that run when Test3 is clicked
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496    term.take_focus().unwrap();
497    term.reset_terminal();
498    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500    assert_eq!(term.history_use(), 0);
501    term.clear();
502    assert_eq!(term.cursor_row(), 0);
503    assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505    term.append("Test\ntext\na\nb\nc\nd");
506    assert_eq!(term.cursor_row(), 5);
507    let hist = term.history_use();
508    term.clear_screen_home(false);
509    assert_eq!(term.cursor_row(), 0);
510    assert_eq!(term.history_use(), hist); // History not changed
511
512    term.append("Test\ntext\na\nb\nc\nd\ne");
513    assert_eq!(term.cursor_row(), 6);
514    term.clear_screen_home(true);
515    assert_eq!(term.cursor_row(), 0);
516
517    term.append("Test\ntext\na\nb\nc\n");
518    assert_eq!(term.cursor_row(), 5);
519    term.clear_to_color(Color::DarkBlue);
520    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521    assert_eq!(term.text_bg_color(), Color::TransparentBg);
522    assert_eq!(term.cursor_row(), 0);
523
524    // Test cursor_home()
525    term.append("Test\n\n\n\n\n\n\n\n\n\n");
526    assert_eq!(term.cursor_row(), 10);
527    term.cursor_home();
528    assert_eq!(term.cursor_row(), 0);
529
530    // Test the widget color
531    assert_eq!(term.color(), Color::Black); // Default
532    term.set_color(Color::DarkGreen);
533    assert_eq!(term.color(), Color::DarkGreen);
534    term.set_color(Color::Black);
535    assert_eq!(term.color(), Color::Black);
536    term.append(
537        "This should be one line of white text on black, embedded into the top of a blue field.\n",
538    );
539
540    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541    term.set_output_translate(OutFlags::OFF);
542    assert_eq!(term.output_translate(), OutFlags::OFF);
543    term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547    term.append("\nDone!\n");
548    term.set_text_attrib(Attrib::Normal);
549}
550
551//--------------------------------------------------------------------------------------
552/// Another set of tests for the ring-buffer access methods
553/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555    let sel_len = term.selection_text_len();
556    let sel = term.selection_text();
557
558    term.take_focus().unwrap();
559    term.reset_terminal();
560    // Test the Utf8Char primitive
561    let uc = Utf8Char::new(b'Q');
562    let uc1 = uc.text_utf8();
563    assert_eq!(&uc1, b"Q");
564    assert_eq!(&uc.attrib(), &Attrib::Normal);
565    assert_eq!(
566        &uc.charflags(),
567        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568    );
569    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570    assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572    let ring_rows = term.ring_rows();
573
574    term.take_focus().unwrap();
575    term.clear_history();
576    assert_eq!(term.history_use(), 0);
577
578    // Subtract row numbers, modulo `rows`
579    fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580        match a - b {
581            n if n < 0 => n + rows,
582            n => n,
583        }
584    }
585    // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587    assert!(term.disp_srow() >= 0);
588    assert!(term.disp_erow() >= 0);
589    assert!(term.hist_srow() >= 0);
590    assert!(term.hist_erow() >= 0);
591    assert!(term.offset() >= 0);
592    assert!(term.disp_srow() <= ring_rows);
593    assert!(term.disp_erow() <= ring_rows);
594    assert!(term.hist_srow() <= ring_rows);
595    assert!(term.hist_erow() <= ring_rows);
596    assert!(term.offset() <= ring_rows);
597
598    assert_eq!(term.ring_srow(), 0);
599    assert_eq!(term.ring_erow(), ring_rows - 1);
600    assert_eq!(
601        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602        term.display_rows()
603    );
604    assert_eq!(
605        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606        term.history_rows()
607    );
608
609    assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610    assert_eq!(term.ring_srow(), 0);
611
612    /// Local function to read back all rows from the display into a long string.
613    /// Does not include scrollback history.
614    /// Trims trailing blanks on each line
615    fn read_disp(term: &Terminal) -> String {
616        let rows = term.display_rows();
617        let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618        for row in 0..rows {
619            let r = term.u8c_disp_row(row).trim();
620            // Iterate through a row, accumulating [u8]
621            for c in r.iter() {
622                // Note: Sometimes utf-8 length is > 1
623                text.extend_from_slice(c.text_utf8());
624            }
625            text.extend_from_slice(b"\n");
626        }
627        // Return the result as a string
628        std::str::from_utf8(&text).unwrap().to_string()
629    }
630
631    term.clear();
632    term.append("Top line  ↑ (up-arrow)");
633    term.set_text_attrib(Attrib::Underline);
634    term.append("  ");
635    term.set_text_attrib(Attrib::Normal);
636    term.append("  \n");
637    let mut text_out = read_disp(term);
638    // Trim trailing empty lines
639    text_out = text_out.trim_end_matches(&"\n").to_string();
640    // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642    assert_eq!(text_out, "Top line  ↑ (up-arrow)  ");
643    let r = term.u8c_disp_row(0);
644    assert_eq!(r.col(0).text_utf8(), b"T");
645    assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646    assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647    let r = term.u8c_disp_row(1);
648    assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649    assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651    // Clear the screen again, then append test text, then read it back and compare
652    let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656            Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659    term.clear_history();
660    term.clear();
661    let bg_save = term.text_bg_color();
662    let fg_save = term.text_fg_color();
663    term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664    term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665    term.append(test_text);
666    term.set_text_bg_color(bg_save);
667    term.set_text_fg_color(fg_save);
668
669    let mut text_out = read_disp(term);
670    // Trim trailing empty lines
671    text_out = text_out.trim_end_matches(&"\n").to_string();
672    assert_eq!(test_text, text_out);
673
674    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676    assert_eq!(term.ring_srow(), 0);
677    assert_eq!(term.ring_erow(), ring_rows - 1);
678    assert_eq!(
679        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680        term.display_rows()
681    );
682    assert_eq!(
683        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684        term.history_rows()
685    );
686
687    term.append(&format!(
688        "\n\nScreen has {} rows of {} columns.\n",
689        term.display_rows(),
690        term.display_columns()
691    ));
692
693    term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
Source

pub fn set_text_fg_color_default(&mut self, color: Color)

Set the default text foreground color used by <ESC>c, <ESC>[0m, and reset_terminal(). Does not affect the ‘current’ text fg color; use set_text_fg_color(Fl_Color) to set that.

Examples found in repository?
examples/terminal.rs (line 317)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn text_fg_color_default(&self) -> Color

Return the default text foreground color.

Examples found in repository?
examples/terminal.rs (line 312)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn set_text_fg_color_xterm(&mut self, color: XtermColor)

Sets the foreground text color as one of the 8 ‘xterm color’ values.

This will be the foreground color used for all newly printed text, similar to the <ESC>[#m escape sequence, where # is between 30 and 37.

This color will be reset to the default bg color if reset_terminal() is called, or by <ESC>c, <ESC>[0m, etc.

The xterm color intensity values can be influenced by the Dim/Bold/Normal modes (which can be set with e.g. <ESC>[1m, textattrib(), etc), so the actual RGB values of these colors allow room for Dim/Bold to influence their brightness. For instance, “Normal Red” is not full brightness to allow “Bold Red” to be brighter. This goes for all colors except ‘Black’, which is not influenced by Dim or Bold; Black is always Black.

The 8 color xterm values are: 0 = Black, 1 = Red, 2 = Green, 3 = Yellow, 4 = Blue,5 = Magenta, 6 = Cyan, 7 = White

Examples found in repository?
examples/terminal.rs (line 719)
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700    term.take_focus().unwrap();
701
702    // Test the attr_fg_color and attr_bg_color methods.
703    // Put a single character 'A' into the buffer and check it
704    term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705    term.set_text_bg_color(Color::TransparentBg);
706    term.set_text_fg_color(Color::XtermWhite);
707    term.append("A");
708    let r = &term.u8c_disp_row(0);
709    let uc = r.col(0);
710    assert_eq!(uc.text_utf8(), b"A");
711    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712    assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713    assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714    assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715    assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717    // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718    term.clear();
719    term.set_text_fg_color_xterm(XtermColor::White);
720    term.set_text_bg_color_xterm(XtermColor::Black);
721    assert_eq!(term.text_attrib(), Attrib::Normal);
722
723    assert!(term.ansi());
724    term.append("B\x1b[32mC\x1b[1mD\n");
725
726    let r = &term.u8c_disp_row(0);
727    let uc = r.col(0);
728    assert_eq!(uc.text_utf8(), b"B");
729    assert!(uc.is_char(b'B'));
730    assert!(!uc.is_char(b'A'));
731    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735    assert_eq!(
736        &uc.charflags(),
737        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738    );
739
740    let uc = r.col(1);
741    assert_eq!(uc.text_utf8(), b"C");
742    assert!(uc.is_char(b'C'));
743    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747    assert_eq!(
748        &uc.charflags(),
749        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750    );
751
752    let uc = r.col(2);
753    assert_eq!(uc.text_utf8(), b"D");
754    assert!(uc.is_char(b'D'));
755    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757    assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759    assert_eq!(
760        &uc.attr_fgcolor(Some(term)),
761        &Color::from_rgb(0x20, 0xf0, 0x20)
762    );
763    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764    assert_eq!(
765        &uc.charflags(),
766        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767    );
768
769    // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770    term.clear();
771    term.set_text_fg_color_xterm(XtermColor::White);
772    term.set_text_bg_color_xterm(XtermColor::Black);
773    term.set_text_attrib(Attrib::Normal);
774    assert_eq!(term.text_attrib(), Attrib::Normal);
775
776    assert!(term.ansi());
777    term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779    let r = &term.u8c_disp_row(0);
780    let uc = r.col(0);
781    assert_eq!(uc.text_utf8(), b"B");
782    assert!(uc.is_char(b'B'));
783    assert!(!uc.is_char(b'A'));
784    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788    assert_eq!(
789        &uc.charflags(),
790        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791    );
792
793    let uc = r.col(1);
794    assert_eq!(uc.text_utf8(), b"C");
795    assert!(uc.is_char(b'C'));
796    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800    assert_eq!(
801        &uc.charflags(),
802        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803    );
804
805    let uc = r.col(2);
806    assert_eq!(uc.text_utf8(), b"D");
807    assert!(uc.is_char(b'D'));
808    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812    assert_eq!(
813        &uc.charflags(),
814        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815    );
816
817    let uc = r.col(3);
818    assert_eq!(uc.text_utf8(), b"E");
819    assert!(uc.is_char(b'E'));
820    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822    assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823    assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824    assert_eq!(
825        &uc.charflags(),
826        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827    );
828
829    // Test some miscellaneous Utf8 constants
830    assert_eq!(uc.length(), 1);
831    assert_eq!(uc.max_utf8(), 4);
832    assert_eq!(uc.pwidth(), 9.0);
833    assert_eq!(uc.pwidth_int(), 9);
834
835    term.set_text_fg_color_xterm(XtermColor::White);
836    term.set_text_bg_color_xterm(XtermColor::Black);
837    term.clear();
838    term.set_text_attrib(Attrib::Normal);
839
840    // Mouse selection functions
841    term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842    term.clear_mouse_selection();
843    assert_eq!(term.get_selection(), None);
844
845    // Play with cursor position
846    term.append("0123456789\n"); // Set up test pattern
847    term.append("ABCDEFGHIJ\n");
848    term.append("abcdefghij\n");
849
850    term.set_cursor_row(1);
851    assert_eq!(term.cursor_row(), 1);
852    term.set_cursor_col(1);
853    assert_eq!(term.cursor_col(), 1);
854    assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856    term.append("----"); // Overwrites text at cursor and moves cursor forward
857    assert_eq!(term.cursor_row(), 1);
858    assert_eq!(term.cursor_col(), 5);
859    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860    term.set_cursor_col(1);
861    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863    term.cursor_up(1, false);
864    assert_eq!(term.cursor_row(), 0);
865    assert_eq!(term.cursor_col(), 1);
866    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868    // Hit top of screen, so nothing happens
869    term.cursor_up(1, false);
870    assert_eq!(term.cursor_row(), 0);
871    assert_eq!(term.cursor_col(), 1);
872    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874    // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875    term.cursor_up(1, true);
876    assert_eq!(term.cursor_row(), 0);
877    assert_eq!(term.cursor_col(), 1);
878    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880    // Go back down to the overwritten text
881    term.cursor_down(2, false);
882    assert_eq!(term.cursor_row(), 2);
883    assert_eq!(term.cursor_col(), 1);
884    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886    // Go right past the overwritten text
887    term.cursor_right(4, false);
888    assert_eq!(term.cursor_row(), 2);
889    assert_eq!(term.cursor_col(), 5);
890    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892    // Go left to the end of the overwritten text
893    term.cursor_left(1);
894    assert_eq!(term.cursor_row(), 2);
895    assert_eq!(term.cursor_col(), 4);
896    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898    // Scroll back down, removing the blank line at the top.
899    // Cursor stays in place, the text moves under it.
900    term.scroll(1);
901    assert_eq!(term.cursor_row(), 2);
902    assert_eq!(term.cursor_col(), 4);
903    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905    // Clear from here to end-of-line
906    term.clear_eol();
907    assert_eq!(term.cursor_row(), 2);
908    assert_eq!(term.cursor_col(), 4);
909    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911    // Now clear from here to start-of-line. Cursor does not move.
912    term.clear_sol();
913    assert_eq!(term.cursor_row(), 2);
914    assert_eq!(term.cursor_col(), 4);
915    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916    term.cursor_left(1);
917    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918    term.set_cursor_col(0);
919    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921    // Clear some lines
922    term.clear_line(1);
923    assert_eq!(term.cursor_row(), 2);
924    assert_eq!(term.cursor_col(), 0);
925    term.set_cursor_row(1);
926    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927    term.set_cursor_row(3);
928    term.clear_cur_line();
929    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930    assert_eq!(term.cursor_row(), 3);
931    assert_eq!(term.cursor_col(), 0);
932
933    term.append("Two lines above are intentionally left blank.\n");
934    assert_eq!(term.cursor_row(), 4);
935    assert_eq!(term.cursor_col(), 0);
936
937    // Set up the test pattern again, then play with insert/delete
938    term.append("0123456789\n");
939    term.append("ABCDEFGHIJ\n");
940    term.append("abcdefghij\n");
941    assert_eq!(term.cursor_row(), 7);
942
943    term.set_cursor_row(4);
944    term.set_cursor_col(4);
945    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947    term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949    term.cursor_right(5, false);
950    assert_eq!(term.cursor_col(), 9);
951    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953    // Insert two blank rows above cursor. Cursor stays put.
954    term.insert_rows(2);
955    assert_eq!(term.cursor_row(), 4);
956    assert_eq!(term.cursor_col(), 9);
957    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958    term.cursor_down(2, false); // Go down to find our text again
959    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961    // Go back to the beginning of the inserted 'x' characters and delete them.
962    term.cursor_left(5);
963    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964    term.delete_cur_chars(5);
965    assert_eq!(term.cursor_row(), 6);
966    assert_eq!(term.cursor_col(), 4);
967    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969    term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970    term.cursor_down(1, false);
971    term.cursor_left(2);
972    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974    term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975    assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976    term.cursor_up(1, false);
977    term.delete_rows(2); // Delete remains of test pattern
978
979    term.set_text_attrib(Attrib::Bold);
980    term.insert_char_eol('-', 3, 15, 20);
981    term.set_cursor_row(3);
982    term.set_cursor_col(15);
983    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984    assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986    term.set_text_attrib(Attrib::Italic);
987    term.append(" and all lines below");
988    term.set_text_attrib(Attrib::Normal);
989    term.cursor_down(1, false);
990
991    let mut hsb = term.hscrollbar();
992    let mut sb = term.scrollbar();
993    hsb.set_value(100.0);
994    assert_eq!(hsb.value(), 100.0);
995    sb.set_value(50.0);
996    assert_eq!(sb.value(), 50.0);
997    hsb.set_value(0.0);
998    assert_eq!(hsb.value(), 0.0);
999    sb.set_value(0.0);
1000    assert_eq!(sb.value(), 0.0);
1001}
Source

pub fn text_font(&self) -> Font

Get the text font

Examples found in repository?
examples/terminal.rs (line 340)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn set_text_font(&mut self, font: Font)

Sets the font used for all text displayed in the terminal. This affects all existing text (in display and history) as well as any newly printed text. Only monospace fonts are recommended.

Examples found in repository?
examples/terminal.rs (line 343)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn text_size(&self) -> i32

Return text font size used to draw all text in the terminal.

Examples found in repository?
examples/terminal.rs (line 348)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn set_text_size(&mut self, val: i32)

Sets the font size used for all text displayed in the terminal. This affects all existing text (in display and history) as well as any newly printed text. Changing this will affect the display_rows() and display_columns().

Examples found in repository?
examples/terminal.rs (line 355)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn text(&self, lines_below_cursor: bool) -> String

Return a string copy of all lines in the terminal (including history).

If lines_below_cursor is false, lines below the cursor on down to the bottom of the display are ignored, and not included in the returned string.

If lines_below_cursor is true, then all lines in the display are returned including any below the cursor, even if all are blank.

Example use:

    use fltk::{prelude::*, terminal::Terminal};
    let mut tty = Terminal::new(0, 0, 400, 300, None);
     
    let s = tty.text(true);   // get a copy of the terminal's contents
    println!("Terminal's contents:\n{}", s);
Examples found in repository?
examples/terminal.rs (line 113)
20fn main() {
21    let app = fltk::app::App::default();
22
23    // Set panic handler for main thread (will become UI thread)
24    std::panic::set_hook(Box::new({
25        |e| {
26            eprintln!("!!!!PANIC!!!!{:#?}", e);
27            error_box(e.to_string()); // Only works from the UI thread
28            std::process::exit(2);
29        }
30    }));
31
32    let mut main_win = Window::new(
33        2285,
34        180,
35        WIN_WIDTH,
36        WIN_HEIGHT,
37        "FLTK/Terminal Rust wrapper test",
38    );
39    main_win.set_type(WindowType::Double);
40    main_win.make_resizable(true);
41
42    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45    term.set_label("term");
46    main_win.resizable(&term);
47    term.set_label_type(LabelType::None);
48
49    let idx = menu_bar.add_choice("Test&1");
50    menu_bar.at(idx).unwrap().set_callback({
51        let mut term1 = term.clone();
52        move |c| mb_test1_cb(c, &mut term1)
53    });
54    menu_bar
55        .at(idx)
56        .unwrap()
57        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59    let idx = menu_bar.add_choice("Test&2");
60    menu_bar.at(idx).unwrap().set_callback({
61        let mut term1 = term.clone();
62        move |c| mb_test2_cb(c, &mut term1)
63    });
64    menu_bar
65        .at(idx)
66        .unwrap()
67        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69    let idx = menu_bar.add_choice("Test&3");
70    menu_bar.at(idx).unwrap().set_callback({
71        let mut term1 = term.clone();
72        move |c| mb_test3_cb(c, &mut term1)
73    });
74    menu_bar
75        .at(idx)
76        .unwrap()
77        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79    let idx = menu_bar.add_choice("Test&4");
80    menu_bar.at(idx).unwrap().set_callback({
81        let mut term1 = term.clone();
82        move |c| mb_test4_cb(c, &mut term1)
83    });
84    menu_bar
85        .at(idx)
86        .unwrap()
87        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89    let idx = menu_bar.add_choice("Test&5");
90    menu_bar.at(idx).unwrap().set_callback({
91        let mut term1 = term.clone();
92        move |c| mb_test5_cb(c, &mut term1)
93    });
94    menu_bar
95        .at(idx)
96        .unwrap()
97        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99    menu_bar.end();
100
101    main_win.end();
102    main_win.show();
103
104    // Worker thread that drives the startup tests
105    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106        let mut term = term.clone();
107        move || {
108            println!("Startup tests\n");
109            term.append("Startup tests\n\n");
110            term.append("<tmp>\n"); // This line will be overwritten later
111
112            term.cursor_up(2, false);
113            assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114            assert_eq!(
115                term.text(true),
116                "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117            );
118
119            // Testing ansi() and set_ansi() methods
120            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122            term.set_ansi(false);
123            assert!(!term.ansi());
124            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126            term.append_u8(b"Appending u8 array\n");
127            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128            term.set_ansi(true); // Restore ANSI state
129
130            // Play with the horizontal scrollbar
131            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132            term.set_hscrollbar_style(ScrollbarStyle::ON);
133            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135            // Test show_unknown() as incidental part of testing append methods
136            term.set_show_unknown(true);
137            assert!(term.show_unknown());
138            term.append_ascii(
139                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140            );
141            term.set_show_unknown(false);
142            assert!(!term.show_unknown());
143
144            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147            let r = term.cursor_row();
148            assert_eq!(term.cursor_col(), 0);
149            term.append(&format!("Testing cursor row/col {r}"));
150            assert_eq!(term.cursor_col(), 24);
151            assert_eq!(term.cursor_row(), r);
152
153            // Test cursor color methods
154            assert_eq!(
155                term.cursor_bg_color(),
156                Color::XtermGreen,
157                "Default cursor bg at startup"
158            );
159            term.set_cursor_bg_color(Color::Red);
160            assert_eq!(term.cursor_bg_color(), Color::Red);
161            term.set_cursor_fg_color(Color::Blue);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::Blue);
164            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169            // The default display_rows() will derive from the window size
170            let dr = term.display_rows();
171            let height = term.h();
172            assert_eq!(height, term.h());
173            assert!(dr > 20, "Default display_rows at startup");
174            term.resize(term.x(), term.y(), term.w(), height * 2);
175            assert_eq!(term.h(), height * 2);
176            assert_eq!(height * 2, term.h());
177            assert!(term.display_rows() > dr);
178            term.resize(term.x(), term.y(), term.w(), height);
179
180            // The default display_columns() will derive from the window size
181            let dc = term.display_columns();
182            assert!(dc > 80, "Default display_rows at startup");
183            term.set_display_columns(200);
184            assert_eq!(term.display_columns(), 200);
185            term.append("\n         1         2         3         4         5         6         7         8         9");
186            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188            term.set_display_columns(90);
189            assert_eq!(term.display_columns(), 90);
190            term.set_display_columns(dc); // Set back to default
191            assert_eq!(term.display_columns(), dc);
192
193            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194            term.set_history_rows(50);
195            assert_eq!(term.history_rows(), 50);
196            term.set_history_rows(100); // Set back to default
197            assert_eq!(term.history_rows(), 100);
198
199            let hu = term.history_use();
200            term.append(&format!(
201                "history_use = {hu} (it's not clear what this means)\n"
202            ));
203            // assert_eq!(term.history_use(), hu+1);
204
205            term.append(&format!(
206                "margins = b:{} l:{} r:{} t{}\n",
207                term.margin_bottom(),
208                term.margin_left(),
209                term.margin_right(),
210                term.margin_top()
211            ));
212            assert_eq!(term.margin_bottom(), 3);
213            assert_eq!(term.margin_left(), 3);
214            assert_eq!(term.margin_right(), 3);
215            assert_eq!(term.margin_top(), 3);
216
217            term.set_margin_bottom(5);
218            term.set_margin_left(10);
219            term.set_margin_right(15);
220            term.set_margin_top(20);
221            assert_eq!(term.margin_bottom(), 5);
222            assert_eq!(term.margin_left(), 10);
223            assert_eq!(term.margin_right(), 15);
224            assert_eq!(term.margin_top(), 20);
225
226            term.append("Single character: '");
227            term.print_char('X');
228            term.append("', single UTF-8 character: '");
229            term.print_char_utf8('↑');
230            term.append("'\n");
231
232            let rr = term.redraw_rate();
233            assert_eq!(rr, 0.1, "Default redraw rate at startup");
234            term.append(&format!("Redraw rate {rr}\n"));
235            term.set_redraw_rate(1.0);
236            assert_eq!(term.redraw_rate(), 1.0);
237            term.set_redraw_rate(rr);
238            assert_eq!(term.redraw_rate(), rr);
239
240            let rs = term.redraw_style();
241            term.append(&format!("Redraw style {rs:?}\n"));
242            assert_eq!(
243                rs,
244                RedrawStyle::RateLimited,
245                "Default redraw style at startup"
246            );
247            term.set_redraw_style(RedrawStyle::NoRedraw);
248            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249            term.set_redraw_style(rs);
250            assert_eq!(term.redraw_style(), rs);
251
252            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253            assert_eq!(
254                RedrawStyle::NoRedraw.bits(),
255                0x0000,
256                "RedrawStyle enum values have been reassigned"
257            );
258            assert_eq!(
259                RedrawStyle::RateLimited.bits(),
260                0x0001,
261                "RedrawStyle enum values have been reassigned"
262            );
263            assert_eq!(
264                RedrawStyle::PerWrite.bits(),
265                0x0002,
266                "RedrawStyle enum values have been reassigned"
267            );
268
269            let sb = term.scrollbar();
270            let hsb = term.hscrollbar();
271            // Both vertical and horizontal scrollbars are at zero
272            assert_eq!(sb.value(), 0.0);
273            assert_eq!(hsb.value(), 0.0);
274            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276            term.append(&format!(
277                "Scrollbar actual size {}\n",
278                term.scrollbar_actual_size()
279            ));
280            assert_eq!(term.scrollbar_actual_size(), 16);
281            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282            assert_eq!(
283                term.scrollbar_size(),
284                0,
285                "Default scrollbar size at startup"
286            );
287            term.set_scrollbar_size(40);
288            assert_eq!(term.scrollbar_size(), 40);
289            assert_eq!(term.scrollbar_actual_size(), 40);
290            term.append(&format!(
291                "Scrollbar actual size {}\n",
292                term.scrollbar_actual_size()
293            ));
294            term.set_scrollbar_size(0); // Restore default
295            assert_eq!(term.scrollbar_size(), 0);
296            assert_eq!(term.scrollbar_actual_size(), 16);
297
298            let sfc = term.selection_fg_color();
299            let sbc = term.selection_bg_color();
300            assert_eq!(sfc, Color::Black);
301            assert_eq!(sbc, Color::White);
302            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303            term.set_selection_fg_color(Color::Green);
304            term.set_selection_bg_color(Color::DarkBlue);
305            assert_eq!(term.selection_fg_color(), Color::Green);
306            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307            term.set_selection_fg_color(sfc);
308            term.set_selection_bg_color(sbc);
309            assert_eq!(term.selection_fg_color(), Color::Black);
310            assert_eq!(term.selection_bg_color(), Color::White);
311
312            let tfcd = term.text_fg_color_default();
313            let tbcd = term.text_bg_color_default();
314            assert_eq!(tfcd, Color::XtermWhite);
315            assert_eq!(tbcd, Color::TransparentBg);
316            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317            term.set_text_fg_color_default(Color::Green);
318            term.set_text_bg_color_default(Color::DarkBlue);
319            assert_eq!(term.text_fg_color_default(), Color::Green);
320            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321            term.set_text_fg_color_default(tfcd);
322            term.set_text_bg_color_default(tbcd);
323            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326            let tfc = term.text_fg_color();
327            let tbc = term.text_bg_color();
328            assert_eq!(tfc, Color::XtermWhite);
329            assert_eq!(tbc, Color::TransparentBg);
330            term.append(&format!("Text colors: {sfc} {sbc}\n"));
331            term.set_text_fg_color(Color::Green);
332            term.set_text_bg_color(Color::DarkBlue);
333            assert_eq!(term.text_fg_color(), Color::Green);
334            assert_eq!(term.text_bg_color(), Color::DarkBlue);
335            term.set_text_fg_color(tfc);
336            term.set_text_bg_color(tbc);
337            assert_eq!(term.text_fg_color(), Color::XtermWhite);
338            assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340            let tf = term.text_font();
341            term.append(&format!("Text font: {tf:?}\n"));
342            assert_eq!(tf, Font::Courier);
343            term.set_text_font(Font::Screen);
344            assert_eq!(term.text_font(), Font::Screen);
345            term.set_text_font(tf);
346            assert_eq!(term.text_font(), Font::Courier);
347
348            let ts = term.text_size();
349            let r = term.h_to_row(100);
350            let c = term.w_to_col(100);
351            term.append(&format!(
352                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353            ));
354            assert_eq!(ts, 14);
355            term.set_text_size(30);
356            assert_eq!(term.text_size(), 30);
357            term.append(&format!(
358                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359                term.text_size(),
360                term.h_to_row(100),
361                term.w_to_col(100)
362            ));
363            term.set_text_size(ts);
364            assert_eq!(term.text_size(), ts);
365            term.append(&format!(
366                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367                term.text_size(),
368                term.h_to_row(100),
369                term.w_to_col(100)
370            ));
371
372            // Keyboard handler
373            term.handle({
374                move |term, e| {
375                    match e {
376                        fltk::enums::Event::KeyDown
377                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
378                        {
379                            // false to let FLTK handle ESC. true to hide ESC
380                            false
381                        }
382
383                        fltk::enums::Event::KeyDown
384                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385                        {
386                            // We handle control keystroke
387                            let k = fltk::app::event_text().unwrap();
388                            term.append_utf8(&k);
389                            true
390                        }
391
392                        fltk::enums::Event::KeyDown
393                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394                        {
395                            // We handle normal printable keystroke
396                            let k = fltk::app::event_text().unwrap();
397                            term.take_focus().unwrap();
398                            term.append(&k);
399                            true
400                        }
401
402                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403                        // We can do this, or else ignore them (return false)
404                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405                        //     term.redraw();
406                        //     true
407                        // }
408                        _ => false, // Let FLTK handle everything else
409                    }
410                }
411            });
412
413            let attr_save = term.text_attrib();
414            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415            term.append("\nStartup tests complete. Keyboard is live.\n");
416            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417            term.set_text_attrib(attr_save);
418            assert_eq!(term.text_attrib(), attr_save);
419            term.redraw();
420        }
421    });
422
423    app.run().unwrap();
424}
Source

pub fn selection_text(&self) -> Option<String>

Return text selection (for copy()/paste() operations)

Examples found in repository?
examples/terminal.rs (line 556)
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555    let sel_len = term.selection_text_len();
556    let sel = term.selection_text();
557
558    term.take_focus().unwrap();
559    term.reset_terminal();
560    // Test the Utf8Char primitive
561    let uc = Utf8Char::new(b'Q');
562    let uc1 = uc.text_utf8();
563    assert_eq!(&uc1, b"Q");
564    assert_eq!(&uc.attrib(), &Attrib::Normal);
565    assert_eq!(
566        &uc.charflags(),
567        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568    );
569    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570    assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572    let ring_rows = term.ring_rows();
573
574    term.take_focus().unwrap();
575    term.clear_history();
576    assert_eq!(term.history_use(), 0);
577
578    // Subtract row numbers, modulo `rows`
579    fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580        match a - b {
581            n if n < 0 => n + rows,
582            n => n,
583        }
584    }
585    // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587    assert!(term.disp_srow() >= 0);
588    assert!(term.disp_erow() >= 0);
589    assert!(term.hist_srow() >= 0);
590    assert!(term.hist_erow() >= 0);
591    assert!(term.offset() >= 0);
592    assert!(term.disp_srow() <= ring_rows);
593    assert!(term.disp_erow() <= ring_rows);
594    assert!(term.hist_srow() <= ring_rows);
595    assert!(term.hist_erow() <= ring_rows);
596    assert!(term.offset() <= ring_rows);
597
598    assert_eq!(term.ring_srow(), 0);
599    assert_eq!(term.ring_erow(), ring_rows - 1);
600    assert_eq!(
601        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602        term.display_rows()
603    );
604    assert_eq!(
605        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606        term.history_rows()
607    );
608
609    assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610    assert_eq!(term.ring_srow(), 0);
611
612    /// Local function to read back all rows from the display into a long string.
613    /// Does not include scrollback history.
614    /// Trims trailing blanks on each line
615    fn read_disp(term: &Terminal) -> String {
616        let rows = term.display_rows();
617        let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618        for row in 0..rows {
619            let r = term.u8c_disp_row(row).trim();
620            // Iterate through a row, accumulating [u8]
621            for c in r.iter() {
622                // Note: Sometimes utf-8 length is > 1
623                text.extend_from_slice(c.text_utf8());
624            }
625            text.extend_from_slice(b"\n");
626        }
627        // Return the result as a string
628        std::str::from_utf8(&text).unwrap().to_string()
629    }
630
631    term.clear();
632    term.append("Top line  ↑ (up-arrow)");
633    term.set_text_attrib(Attrib::Underline);
634    term.append("  ");
635    term.set_text_attrib(Attrib::Normal);
636    term.append("  \n");
637    let mut text_out = read_disp(term);
638    // Trim trailing empty lines
639    text_out = text_out.trim_end_matches(&"\n").to_string();
640    // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642    assert_eq!(text_out, "Top line  ↑ (up-arrow)  ");
643    let r = term.u8c_disp_row(0);
644    assert_eq!(r.col(0).text_utf8(), b"T");
645    assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646    assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647    let r = term.u8c_disp_row(1);
648    assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649    assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651    // Clear the screen again, then append test text, then read it back and compare
652    let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656            Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659    term.clear_history();
660    term.clear();
661    let bg_save = term.text_bg_color();
662    let fg_save = term.text_fg_color();
663    term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664    term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665    term.append(test_text);
666    term.set_text_bg_color(bg_save);
667    term.set_text_fg_color(fg_save);
668
669    let mut text_out = read_disp(term);
670    // Trim trailing empty lines
671    text_out = text_out.trim_end_matches(&"\n").to_string();
672    assert_eq!(test_text, text_out);
673
674    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676    assert_eq!(term.ring_srow(), 0);
677    assert_eq!(term.ring_erow(), ring_rows - 1);
678    assert_eq!(
679        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680        term.display_rows()
681    );
682    assert_eq!(
683        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684        term.history_rows()
685    );
686
687    term.append(&format!(
688        "\n\nScreen has {} rows of {} columns.\n",
689        term.display_rows(),
690        term.display_columns()
691    ));
692
693    term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
Source

pub fn selection_text_len(&self) -> i32

Return byte length of all UTF-8 chars in selection, or 0 if no selection. NOTE: Length includes trailing white on each line.

Examples found in repository?
examples/terminal.rs (line 555)
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555    let sel_len = term.selection_text_len();
556    let sel = term.selection_text();
557
558    term.take_focus().unwrap();
559    term.reset_terminal();
560    // Test the Utf8Char primitive
561    let uc = Utf8Char::new(b'Q');
562    let uc1 = uc.text_utf8();
563    assert_eq!(&uc1, b"Q");
564    assert_eq!(&uc.attrib(), &Attrib::Normal);
565    assert_eq!(
566        &uc.charflags(),
567        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568    );
569    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570    assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572    let ring_rows = term.ring_rows();
573
574    term.take_focus().unwrap();
575    term.clear_history();
576    assert_eq!(term.history_use(), 0);
577
578    // Subtract row numbers, modulo `rows`
579    fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580        match a - b {
581            n if n < 0 => n + rows,
582            n => n,
583        }
584    }
585    // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587    assert!(term.disp_srow() >= 0);
588    assert!(term.disp_erow() >= 0);
589    assert!(term.hist_srow() >= 0);
590    assert!(term.hist_erow() >= 0);
591    assert!(term.offset() >= 0);
592    assert!(term.disp_srow() <= ring_rows);
593    assert!(term.disp_erow() <= ring_rows);
594    assert!(term.hist_srow() <= ring_rows);
595    assert!(term.hist_erow() <= ring_rows);
596    assert!(term.offset() <= ring_rows);
597
598    assert_eq!(term.ring_srow(), 0);
599    assert_eq!(term.ring_erow(), ring_rows - 1);
600    assert_eq!(
601        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602        term.display_rows()
603    );
604    assert_eq!(
605        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606        term.history_rows()
607    );
608
609    assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610    assert_eq!(term.ring_srow(), 0);
611
612    /// Local function to read back all rows from the display into a long string.
613    /// Does not include scrollback history.
614    /// Trims trailing blanks on each line
615    fn read_disp(term: &Terminal) -> String {
616        let rows = term.display_rows();
617        let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618        for row in 0..rows {
619            let r = term.u8c_disp_row(row).trim();
620            // Iterate through a row, accumulating [u8]
621            for c in r.iter() {
622                // Note: Sometimes utf-8 length is > 1
623                text.extend_from_slice(c.text_utf8());
624            }
625            text.extend_from_slice(b"\n");
626        }
627        // Return the result as a string
628        std::str::from_utf8(&text).unwrap().to_string()
629    }
630
631    term.clear();
632    term.append("Top line  ↑ (up-arrow)");
633    term.set_text_attrib(Attrib::Underline);
634    term.append("  ");
635    term.set_text_attrib(Attrib::Normal);
636    term.append("  \n");
637    let mut text_out = read_disp(term);
638    // Trim trailing empty lines
639    text_out = text_out.trim_end_matches(&"\n").to_string();
640    // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642    assert_eq!(text_out, "Top line  ↑ (up-arrow)  ");
643    let r = term.u8c_disp_row(0);
644    assert_eq!(r.col(0).text_utf8(), b"T");
645    assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646    assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647    let r = term.u8c_disp_row(1);
648    assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649    assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651    // Clear the screen again, then append test text, then read it back and compare
652    let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656            Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659    term.clear_history();
660    term.clear();
661    let bg_save = term.text_bg_color();
662    let fg_save = term.text_fg_color();
663    term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664    term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665    term.append(test_text);
666    term.set_text_bg_color(bg_save);
667    term.set_text_fg_color(fg_save);
668
669    let mut text_out = read_disp(term);
670    // Trim trailing empty lines
671    text_out = text_out.trim_end_matches(&"\n").to_string();
672    assert_eq!(test_text, text_out);
673
674    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676    assert_eq!(term.ring_srow(), 0);
677    assert_eq!(term.ring_erow(), ring_rows - 1);
678    assert_eq!(
679        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680        term.display_rows()
681    );
682    assert_eq!(
683        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684        term.history_rows()
685    );
686
687    term.append(&format!(
688        "\n\nScreen has {} rows of {} columns.\n",
689        term.display_rows(),
690        term.display_columns()
691    ));
692
693    term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
Source

pub fn disp_erow(&self) -> i32

Return the ending row# in the display area.

Examples found in repository?
examples/terminal.rs (line 588)
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555    let sel_len = term.selection_text_len();
556    let sel = term.selection_text();
557
558    term.take_focus().unwrap();
559    term.reset_terminal();
560    // Test the Utf8Char primitive
561    let uc = Utf8Char::new(b'Q');
562    let uc1 = uc.text_utf8();
563    assert_eq!(&uc1, b"Q");
564    assert_eq!(&uc.attrib(), &Attrib::Normal);
565    assert_eq!(
566        &uc.charflags(),
567        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568    );
569    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570    assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572    let ring_rows = term.ring_rows();
573
574    term.take_focus().unwrap();
575    term.clear_history();
576    assert_eq!(term.history_use(), 0);
577
578    // Subtract row numbers, modulo `rows`
579    fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580        match a - b {
581            n if n < 0 => n + rows,
582            n => n,
583        }
584    }
585    // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587    assert!(term.disp_srow() >= 0);
588    assert!(term.disp_erow() >= 0);
589    assert!(term.hist_srow() >= 0);
590    assert!(term.hist_erow() >= 0);
591    assert!(term.offset() >= 0);
592    assert!(term.disp_srow() <= ring_rows);
593    assert!(term.disp_erow() <= ring_rows);
594    assert!(term.hist_srow() <= ring_rows);
595    assert!(term.hist_erow() <= ring_rows);
596    assert!(term.offset() <= ring_rows);
597
598    assert_eq!(term.ring_srow(), 0);
599    assert_eq!(term.ring_erow(), ring_rows - 1);
600    assert_eq!(
601        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602        term.display_rows()
603    );
604    assert_eq!(
605        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606        term.history_rows()
607    );
608
609    assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610    assert_eq!(term.ring_srow(), 0);
611
612    /// Local function to read back all rows from the display into a long string.
613    /// Does not include scrollback history.
614    /// Trims trailing blanks on each line
615    fn read_disp(term: &Terminal) -> String {
616        let rows = term.display_rows();
617        let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618        for row in 0..rows {
619            let r = term.u8c_disp_row(row).trim();
620            // Iterate through a row, accumulating [u8]
621            for c in r.iter() {
622                // Note: Sometimes utf-8 length is > 1
623                text.extend_from_slice(c.text_utf8());
624            }
625            text.extend_from_slice(b"\n");
626        }
627        // Return the result as a string
628        std::str::from_utf8(&text).unwrap().to_string()
629    }
630
631    term.clear();
632    term.append("Top line  ↑ (up-arrow)");
633    term.set_text_attrib(Attrib::Underline);
634    term.append("  ");
635    term.set_text_attrib(Attrib::Normal);
636    term.append("  \n");
637    let mut text_out = read_disp(term);
638    // Trim trailing empty lines
639    text_out = text_out.trim_end_matches(&"\n").to_string();
640    // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642    assert_eq!(text_out, "Top line  ↑ (up-arrow)  ");
643    let r = term.u8c_disp_row(0);
644    assert_eq!(r.col(0).text_utf8(), b"T");
645    assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646    assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647    let r = term.u8c_disp_row(1);
648    assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649    assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651    // Clear the screen again, then append test text, then read it back and compare
652    let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656            Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659    term.clear_history();
660    term.clear();
661    let bg_save = term.text_bg_color();
662    let fg_save = term.text_fg_color();
663    term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664    term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665    term.append(test_text);
666    term.set_text_bg_color(bg_save);
667    term.set_text_fg_color(fg_save);
668
669    let mut text_out = read_disp(term);
670    // Trim trailing empty lines
671    text_out = text_out.trim_end_matches(&"\n").to_string();
672    assert_eq!(test_text, text_out);
673
674    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676    assert_eq!(term.ring_srow(), 0);
677    assert_eq!(term.ring_erow(), ring_rows - 1);
678    assert_eq!(
679        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680        term.display_rows()
681    );
682    assert_eq!(
683        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684        term.history_rows()
685    );
686
687    term.append(&format!(
688        "\n\nScreen has {} rows of {} columns.\n",
689        term.display_rows(),
690        term.display_columns()
691    ));
692
693    term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
Source

pub fn disp_srow(&self) -> i32

Return the starting row# in the display area.

Examples found in repository?
examples/terminal.rs (line 586)
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555    let sel_len = term.selection_text_len();
556    let sel = term.selection_text();
557
558    term.take_focus().unwrap();
559    term.reset_terminal();
560    // Test the Utf8Char primitive
561    let uc = Utf8Char::new(b'Q');
562    let uc1 = uc.text_utf8();
563    assert_eq!(&uc1, b"Q");
564    assert_eq!(&uc.attrib(), &Attrib::Normal);
565    assert_eq!(
566        &uc.charflags(),
567        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568    );
569    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570    assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572    let ring_rows = term.ring_rows();
573
574    term.take_focus().unwrap();
575    term.clear_history();
576    assert_eq!(term.history_use(), 0);
577
578    // Subtract row numbers, modulo `rows`
579    fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580        match a - b {
581            n if n < 0 => n + rows,
582            n => n,
583        }
584    }
585    // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587    assert!(term.disp_srow() >= 0);
588    assert!(term.disp_erow() >= 0);
589    assert!(term.hist_srow() >= 0);
590    assert!(term.hist_erow() >= 0);
591    assert!(term.offset() >= 0);
592    assert!(term.disp_srow() <= ring_rows);
593    assert!(term.disp_erow() <= ring_rows);
594    assert!(term.hist_srow() <= ring_rows);
595    assert!(term.hist_erow() <= ring_rows);
596    assert!(term.offset() <= ring_rows);
597
598    assert_eq!(term.ring_srow(), 0);
599    assert_eq!(term.ring_erow(), ring_rows - 1);
600    assert_eq!(
601        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602        term.display_rows()
603    );
604    assert_eq!(
605        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606        term.history_rows()
607    );
608
609    assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610    assert_eq!(term.ring_srow(), 0);
611
612    /// Local function to read back all rows from the display into a long string.
613    /// Does not include scrollback history.
614    /// Trims trailing blanks on each line
615    fn read_disp(term: &Terminal) -> String {
616        let rows = term.display_rows();
617        let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618        for row in 0..rows {
619            let r = term.u8c_disp_row(row).trim();
620            // Iterate through a row, accumulating [u8]
621            for c in r.iter() {
622                // Note: Sometimes utf-8 length is > 1
623                text.extend_from_slice(c.text_utf8());
624            }
625            text.extend_from_slice(b"\n");
626        }
627        // Return the result as a string
628        std::str::from_utf8(&text).unwrap().to_string()
629    }
630
631    term.clear();
632    term.append("Top line  ↑ (up-arrow)");
633    term.set_text_attrib(Attrib::Underline);
634    term.append("  ");
635    term.set_text_attrib(Attrib::Normal);
636    term.append("  \n");
637    let mut text_out = read_disp(term);
638    // Trim trailing empty lines
639    text_out = text_out.trim_end_matches(&"\n").to_string();
640    // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642    assert_eq!(text_out, "Top line  ↑ (up-arrow)  ");
643    let r = term.u8c_disp_row(0);
644    assert_eq!(r.col(0).text_utf8(), b"T");
645    assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646    assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647    let r = term.u8c_disp_row(1);
648    assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649    assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651    // Clear the screen again, then append test text, then read it back and compare
652    let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656            Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659    term.clear_history();
660    term.clear();
661    let bg_save = term.text_bg_color();
662    let fg_save = term.text_fg_color();
663    term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664    term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665    term.append(test_text);
666    term.set_text_bg_color(bg_save);
667    term.set_text_fg_color(fg_save);
668
669    let mut text_out = read_disp(term);
670    // Trim trailing empty lines
671    text_out = text_out.trim_end_matches(&"\n").to_string();
672    assert_eq!(test_text, text_out);
673
674    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676    assert_eq!(term.ring_srow(), 0);
677    assert_eq!(term.ring_erow(), ring_rows - 1);
678    assert_eq!(
679        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680        term.display_rows()
681    );
682    assert_eq!(
683        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684        term.history_rows()
685    );
686
687    term.append(&format!(
688        "\n\nScreen has {} rows of {} columns.\n",
689        term.display_rows(),
690        term.display_columns()
691    ));
692
693    term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
Source

pub fn hist_erow(&self) -> i32

Return the ending row# of the scrollback history.

Examples found in repository?
examples/terminal.rs (line 586)
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555    let sel_len = term.selection_text_len();
556    let sel = term.selection_text();
557
558    term.take_focus().unwrap();
559    term.reset_terminal();
560    // Test the Utf8Char primitive
561    let uc = Utf8Char::new(b'Q');
562    let uc1 = uc.text_utf8();
563    assert_eq!(&uc1, b"Q");
564    assert_eq!(&uc.attrib(), &Attrib::Normal);
565    assert_eq!(
566        &uc.charflags(),
567        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568    );
569    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570    assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572    let ring_rows = term.ring_rows();
573
574    term.take_focus().unwrap();
575    term.clear_history();
576    assert_eq!(term.history_use(), 0);
577
578    // Subtract row numbers, modulo `rows`
579    fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580        match a - b {
581            n if n < 0 => n + rows,
582            n => n,
583        }
584    }
585    // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587    assert!(term.disp_srow() >= 0);
588    assert!(term.disp_erow() >= 0);
589    assert!(term.hist_srow() >= 0);
590    assert!(term.hist_erow() >= 0);
591    assert!(term.offset() >= 0);
592    assert!(term.disp_srow() <= ring_rows);
593    assert!(term.disp_erow() <= ring_rows);
594    assert!(term.hist_srow() <= ring_rows);
595    assert!(term.hist_erow() <= ring_rows);
596    assert!(term.offset() <= ring_rows);
597
598    assert_eq!(term.ring_srow(), 0);
599    assert_eq!(term.ring_erow(), ring_rows - 1);
600    assert_eq!(
601        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602        term.display_rows()
603    );
604    assert_eq!(
605        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606        term.history_rows()
607    );
608
609    assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610    assert_eq!(term.ring_srow(), 0);
611
612    /// Local function to read back all rows from the display into a long string.
613    /// Does not include scrollback history.
614    /// Trims trailing blanks on each line
615    fn read_disp(term: &Terminal) -> String {
616        let rows = term.display_rows();
617        let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618        for row in 0..rows {
619            let r = term.u8c_disp_row(row).trim();
620            // Iterate through a row, accumulating [u8]
621            for c in r.iter() {
622                // Note: Sometimes utf-8 length is > 1
623                text.extend_from_slice(c.text_utf8());
624            }
625            text.extend_from_slice(b"\n");
626        }
627        // Return the result as a string
628        std::str::from_utf8(&text).unwrap().to_string()
629    }
630
631    term.clear();
632    term.append("Top line  ↑ (up-arrow)");
633    term.set_text_attrib(Attrib::Underline);
634    term.append("  ");
635    term.set_text_attrib(Attrib::Normal);
636    term.append("  \n");
637    let mut text_out = read_disp(term);
638    // Trim trailing empty lines
639    text_out = text_out.trim_end_matches(&"\n").to_string();
640    // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642    assert_eq!(text_out, "Top line  ↑ (up-arrow)  ");
643    let r = term.u8c_disp_row(0);
644    assert_eq!(r.col(0).text_utf8(), b"T");
645    assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646    assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647    let r = term.u8c_disp_row(1);
648    assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649    assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651    // Clear the screen again, then append test text, then read it back and compare
652    let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656            Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659    term.clear_history();
660    term.clear();
661    let bg_save = term.text_bg_color();
662    let fg_save = term.text_fg_color();
663    term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664    term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665    term.append(test_text);
666    term.set_text_bg_color(bg_save);
667    term.set_text_fg_color(fg_save);
668
669    let mut text_out = read_disp(term);
670    // Trim trailing empty lines
671    text_out = text_out.trim_end_matches(&"\n").to_string();
672    assert_eq!(test_text, text_out);
673
674    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676    assert_eq!(term.ring_srow(), 0);
677    assert_eq!(term.ring_erow(), ring_rows - 1);
678    assert_eq!(
679        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680        term.display_rows()
681    );
682    assert_eq!(
683        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684        term.history_rows()
685    );
686
687    term.append(&format!(
688        "\n\nScreen has {} rows of {} columns.\n",
689        term.display_rows(),
690        term.display_columns()
691    ));
692
693    term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
Source

pub fn hist_srow(&self) -> i32

Return the starting row# of the scrollback history.

Examples found in repository?
examples/terminal.rs (line 589)
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555    let sel_len = term.selection_text_len();
556    let sel = term.selection_text();
557
558    term.take_focus().unwrap();
559    term.reset_terminal();
560    // Test the Utf8Char primitive
561    let uc = Utf8Char::new(b'Q');
562    let uc1 = uc.text_utf8();
563    assert_eq!(&uc1, b"Q");
564    assert_eq!(&uc.attrib(), &Attrib::Normal);
565    assert_eq!(
566        &uc.charflags(),
567        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568    );
569    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570    assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572    let ring_rows = term.ring_rows();
573
574    term.take_focus().unwrap();
575    term.clear_history();
576    assert_eq!(term.history_use(), 0);
577
578    // Subtract row numbers, modulo `rows`
579    fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580        match a - b {
581            n if n < 0 => n + rows,
582            n => n,
583        }
584    }
585    // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587    assert!(term.disp_srow() >= 0);
588    assert!(term.disp_erow() >= 0);
589    assert!(term.hist_srow() >= 0);
590    assert!(term.hist_erow() >= 0);
591    assert!(term.offset() >= 0);
592    assert!(term.disp_srow() <= ring_rows);
593    assert!(term.disp_erow() <= ring_rows);
594    assert!(term.hist_srow() <= ring_rows);
595    assert!(term.hist_erow() <= ring_rows);
596    assert!(term.offset() <= ring_rows);
597
598    assert_eq!(term.ring_srow(), 0);
599    assert_eq!(term.ring_erow(), ring_rows - 1);
600    assert_eq!(
601        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602        term.display_rows()
603    );
604    assert_eq!(
605        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606        term.history_rows()
607    );
608
609    assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610    assert_eq!(term.ring_srow(), 0);
611
612    /// Local function to read back all rows from the display into a long string.
613    /// Does not include scrollback history.
614    /// Trims trailing blanks on each line
615    fn read_disp(term: &Terminal) -> String {
616        let rows = term.display_rows();
617        let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618        for row in 0..rows {
619            let r = term.u8c_disp_row(row).trim();
620            // Iterate through a row, accumulating [u8]
621            for c in r.iter() {
622                // Note: Sometimes utf-8 length is > 1
623                text.extend_from_slice(c.text_utf8());
624            }
625            text.extend_from_slice(b"\n");
626        }
627        // Return the result as a string
628        std::str::from_utf8(&text).unwrap().to_string()
629    }
630
631    term.clear();
632    term.append("Top line  ↑ (up-arrow)");
633    term.set_text_attrib(Attrib::Underline);
634    term.append("  ");
635    term.set_text_attrib(Attrib::Normal);
636    term.append("  \n");
637    let mut text_out = read_disp(term);
638    // Trim trailing empty lines
639    text_out = text_out.trim_end_matches(&"\n").to_string();
640    // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642    assert_eq!(text_out, "Top line  ↑ (up-arrow)  ");
643    let r = term.u8c_disp_row(0);
644    assert_eq!(r.col(0).text_utf8(), b"T");
645    assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646    assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647    let r = term.u8c_disp_row(1);
648    assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649    assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651    // Clear the screen again, then append test text, then read it back and compare
652    let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656            Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659    term.clear_history();
660    term.clear();
661    let bg_save = term.text_bg_color();
662    let fg_save = term.text_fg_color();
663    term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664    term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665    term.append(test_text);
666    term.set_text_bg_color(bg_save);
667    term.set_text_fg_color(fg_save);
668
669    let mut text_out = read_disp(term);
670    // Trim trailing empty lines
671    text_out = text_out.trim_end_matches(&"\n").to_string();
672    assert_eq!(test_text, text_out);
673
674    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676    assert_eq!(term.ring_srow(), 0);
677    assert_eq!(term.ring_erow(), ring_rows - 1);
678    assert_eq!(
679        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680        term.display_rows()
681    );
682    assert_eq!(
683        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684        term.history_rows()
685    );
686
687    term.append(&format!(
688        "\n\nScreen has {} rows of {} columns.\n",
689        term.display_rows(),
690        term.display_columns()
691    ));
692
693    term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
Source

pub fn hist_use(&self) -> i32

Return number of rows in use by the scrollback history.

Source

pub fn hist_use_srow(&self) -> i32

Return the starting row of the "in use" scrollback history.

Source

pub fn is_inside_selection(&self, row: i32, col: i32) -> bool

Is global row/column inside the current mouse selection? This is a low-level “protected” function of the fltk library

Source

pub fn is_selection(&self) -> bool

Returns true if there’s a mouse selection.

Source

pub fn offset(&self) -> i32

Returns the current offset into the ring buffer.

Examples found in repository?
examples/terminal.rs (line 591)
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555    let sel_len = term.selection_text_len();
556    let sel = term.selection_text();
557
558    term.take_focus().unwrap();
559    term.reset_terminal();
560    // Test the Utf8Char primitive
561    let uc = Utf8Char::new(b'Q');
562    let uc1 = uc.text_utf8();
563    assert_eq!(&uc1, b"Q");
564    assert_eq!(&uc.attrib(), &Attrib::Normal);
565    assert_eq!(
566        &uc.charflags(),
567        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568    );
569    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570    assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572    let ring_rows = term.ring_rows();
573
574    term.take_focus().unwrap();
575    term.clear_history();
576    assert_eq!(term.history_use(), 0);
577
578    // Subtract row numbers, modulo `rows`
579    fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580        match a - b {
581            n if n < 0 => n + rows,
582            n => n,
583        }
584    }
585    // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587    assert!(term.disp_srow() >= 0);
588    assert!(term.disp_erow() >= 0);
589    assert!(term.hist_srow() >= 0);
590    assert!(term.hist_erow() >= 0);
591    assert!(term.offset() >= 0);
592    assert!(term.disp_srow() <= ring_rows);
593    assert!(term.disp_erow() <= ring_rows);
594    assert!(term.hist_srow() <= ring_rows);
595    assert!(term.hist_erow() <= ring_rows);
596    assert!(term.offset() <= ring_rows);
597
598    assert_eq!(term.ring_srow(), 0);
599    assert_eq!(term.ring_erow(), ring_rows - 1);
600    assert_eq!(
601        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602        term.display_rows()
603    );
604    assert_eq!(
605        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606        term.history_rows()
607    );
608
609    assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610    assert_eq!(term.ring_srow(), 0);
611
612    /// Local function to read back all rows from the display into a long string.
613    /// Does not include scrollback history.
614    /// Trims trailing blanks on each line
615    fn read_disp(term: &Terminal) -> String {
616        let rows = term.display_rows();
617        let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618        for row in 0..rows {
619            let r = term.u8c_disp_row(row).trim();
620            // Iterate through a row, accumulating [u8]
621            for c in r.iter() {
622                // Note: Sometimes utf-8 length is > 1
623                text.extend_from_slice(c.text_utf8());
624            }
625            text.extend_from_slice(b"\n");
626        }
627        // Return the result as a string
628        std::str::from_utf8(&text).unwrap().to_string()
629    }
630
631    term.clear();
632    term.append("Top line  ↑ (up-arrow)");
633    term.set_text_attrib(Attrib::Underline);
634    term.append("  ");
635    term.set_text_attrib(Attrib::Normal);
636    term.append("  \n");
637    let mut text_out = read_disp(term);
638    // Trim trailing empty lines
639    text_out = text_out.trim_end_matches(&"\n").to_string();
640    // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642    assert_eq!(text_out, "Top line  ↑ (up-arrow)  ");
643    let r = term.u8c_disp_row(0);
644    assert_eq!(r.col(0).text_utf8(), b"T");
645    assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646    assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647    let r = term.u8c_disp_row(1);
648    assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649    assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651    // Clear the screen again, then append test text, then read it back and compare
652    let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656            Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659    term.clear_history();
660    term.clear();
661    let bg_save = term.text_bg_color();
662    let fg_save = term.text_fg_color();
663    term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664    term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665    term.append(test_text);
666    term.set_text_bg_color(bg_save);
667    term.set_text_fg_color(fg_save);
668
669    let mut text_out = read_disp(term);
670    // Trim trailing empty lines
671    text_out = text_out.trim_end_matches(&"\n").to_string();
672    assert_eq!(test_text, text_out);
673
674    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676    assert_eq!(term.ring_srow(), 0);
677    assert_eq!(term.ring_erow(), ring_rows - 1);
678    assert_eq!(
679        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680        term.display_rows()
681    );
682    assert_eq!(
683        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684        term.history_rows()
685    );
686
687    term.append(&format!(
688        "\n\nScreen has {} rows of {} columns.\n",
689        term.display_rows(),
690        term.display_columns()
691    ));
692
693    term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
Source

pub fn ring_erow(&self) -> i32

Return the ending row# in the ring buffer (Always ring_rows()-1)

Examples found in repository?
examples/terminal.rs (line 599)
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555    let sel_len = term.selection_text_len();
556    let sel = term.selection_text();
557
558    term.take_focus().unwrap();
559    term.reset_terminal();
560    // Test the Utf8Char primitive
561    let uc = Utf8Char::new(b'Q');
562    let uc1 = uc.text_utf8();
563    assert_eq!(&uc1, b"Q");
564    assert_eq!(&uc.attrib(), &Attrib::Normal);
565    assert_eq!(
566        &uc.charflags(),
567        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568    );
569    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570    assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572    let ring_rows = term.ring_rows();
573
574    term.take_focus().unwrap();
575    term.clear_history();
576    assert_eq!(term.history_use(), 0);
577
578    // Subtract row numbers, modulo `rows`
579    fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580        match a - b {
581            n if n < 0 => n + rows,
582            n => n,
583        }
584    }
585    // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587    assert!(term.disp_srow() >= 0);
588    assert!(term.disp_erow() >= 0);
589    assert!(term.hist_srow() >= 0);
590    assert!(term.hist_erow() >= 0);
591    assert!(term.offset() >= 0);
592    assert!(term.disp_srow() <= ring_rows);
593    assert!(term.disp_erow() <= ring_rows);
594    assert!(term.hist_srow() <= ring_rows);
595    assert!(term.hist_erow() <= ring_rows);
596    assert!(term.offset() <= ring_rows);
597
598    assert_eq!(term.ring_srow(), 0);
599    assert_eq!(term.ring_erow(), ring_rows - 1);
600    assert_eq!(
601        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602        term.display_rows()
603    );
604    assert_eq!(
605        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606        term.history_rows()
607    );
608
609    assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610    assert_eq!(term.ring_srow(), 0);
611
612    /// Local function to read back all rows from the display into a long string.
613    /// Does not include scrollback history.
614    /// Trims trailing blanks on each line
615    fn read_disp(term: &Terminal) -> String {
616        let rows = term.display_rows();
617        let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618        for row in 0..rows {
619            let r = term.u8c_disp_row(row).trim();
620            // Iterate through a row, accumulating [u8]
621            for c in r.iter() {
622                // Note: Sometimes utf-8 length is > 1
623                text.extend_from_slice(c.text_utf8());
624            }
625            text.extend_from_slice(b"\n");
626        }
627        // Return the result as a string
628        std::str::from_utf8(&text).unwrap().to_string()
629    }
630
631    term.clear();
632    term.append("Top line  ↑ (up-arrow)");
633    term.set_text_attrib(Attrib::Underline);
634    term.append("  ");
635    term.set_text_attrib(Attrib::Normal);
636    term.append("  \n");
637    let mut text_out = read_disp(term);
638    // Trim trailing empty lines
639    text_out = text_out.trim_end_matches(&"\n").to_string();
640    // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642    assert_eq!(text_out, "Top line  ↑ (up-arrow)  ");
643    let r = term.u8c_disp_row(0);
644    assert_eq!(r.col(0).text_utf8(), b"T");
645    assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646    assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647    let r = term.u8c_disp_row(1);
648    assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649    assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651    // Clear the screen again, then append test text, then read it back and compare
652    let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656            Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659    term.clear_history();
660    term.clear();
661    let bg_save = term.text_bg_color();
662    let fg_save = term.text_fg_color();
663    term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664    term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665    term.append(test_text);
666    term.set_text_bg_color(bg_save);
667    term.set_text_fg_color(fg_save);
668
669    let mut text_out = read_disp(term);
670    // Trim trailing empty lines
671    text_out = text_out.trim_end_matches(&"\n").to_string();
672    assert_eq!(test_text, text_out);
673
674    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676    assert_eq!(term.ring_srow(), 0);
677    assert_eq!(term.ring_erow(), ring_rows - 1);
678    assert_eq!(
679        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680        term.display_rows()
681    );
682    assert_eq!(
683        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684        term.history_rows()
685    );
686
687    term.append(&format!(
688        "\n\nScreen has {} rows of {} columns.\n",
689        term.display_rows(),
690        term.display_columns()
691    ));
692
693    term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
Source

pub fn ring_srow(&self) -> i32

Return the starting row# in the ring buffer (Always 0)

Examples found in repository?
examples/terminal.rs (line 598)
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555    let sel_len = term.selection_text_len();
556    let sel = term.selection_text();
557
558    term.take_focus().unwrap();
559    term.reset_terminal();
560    // Test the Utf8Char primitive
561    let uc = Utf8Char::new(b'Q');
562    let uc1 = uc.text_utf8();
563    assert_eq!(&uc1, b"Q");
564    assert_eq!(&uc.attrib(), &Attrib::Normal);
565    assert_eq!(
566        &uc.charflags(),
567        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568    );
569    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570    assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572    let ring_rows = term.ring_rows();
573
574    term.take_focus().unwrap();
575    term.clear_history();
576    assert_eq!(term.history_use(), 0);
577
578    // Subtract row numbers, modulo `rows`
579    fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580        match a - b {
581            n if n < 0 => n + rows,
582            n => n,
583        }
584    }
585    // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587    assert!(term.disp_srow() >= 0);
588    assert!(term.disp_erow() >= 0);
589    assert!(term.hist_srow() >= 0);
590    assert!(term.hist_erow() >= 0);
591    assert!(term.offset() >= 0);
592    assert!(term.disp_srow() <= ring_rows);
593    assert!(term.disp_erow() <= ring_rows);
594    assert!(term.hist_srow() <= ring_rows);
595    assert!(term.hist_erow() <= ring_rows);
596    assert!(term.offset() <= ring_rows);
597
598    assert_eq!(term.ring_srow(), 0);
599    assert_eq!(term.ring_erow(), ring_rows - 1);
600    assert_eq!(
601        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602        term.display_rows()
603    );
604    assert_eq!(
605        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606        term.history_rows()
607    );
608
609    assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610    assert_eq!(term.ring_srow(), 0);
611
612    /// Local function to read back all rows from the display into a long string.
613    /// Does not include scrollback history.
614    /// Trims trailing blanks on each line
615    fn read_disp(term: &Terminal) -> String {
616        let rows = term.display_rows();
617        let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618        for row in 0..rows {
619            let r = term.u8c_disp_row(row).trim();
620            // Iterate through a row, accumulating [u8]
621            for c in r.iter() {
622                // Note: Sometimes utf-8 length is > 1
623                text.extend_from_slice(c.text_utf8());
624            }
625            text.extend_from_slice(b"\n");
626        }
627        // Return the result as a string
628        std::str::from_utf8(&text).unwrap().to_string()
629    }
630
631    term.clear();
632    term.append("Top line  ↑ (up-arrow)");
633    term.set_text_attrib(Attrib::Underline);
634    term.append("  ");
635    term.set_text_attrib(Attrib::Normal);
636    term.append("  \n");
637    let mut text_out = read_disp(term);
638    // Trim trailing empty lines
639    text_out = text_out.trim_end_matches(&"\n").to_string();
640    // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642    assert_eq!(text_out, "Top line  ↑ (up-arrow)  ");
643    let r = term.u8c_disp_row(0);
644    assert_eq!(r.col(0).text_utf8(), b"T");
645    assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646    assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647    let r = term.u8c_disp_row(1);
648    assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649    assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651    // Clear the screen again, then append test text, then read it back and compare
652    let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656            Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659    term.clear_history();
660    term.clear();
661    let bg_save = term.text_bg_color();
662    let fg_save = term.text_fg_color();
663    term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664    term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665    term.append(test_text);
666    term.set_text_bg_color(bg_save);
667    term.set_text_fg_color(fg_save);
668
669    let mut text_out = read_disp(term);
670    // Trim trailing empty lines
671    text_out = text_out.trim_end_matches(&"\n").to_string();
672    assert_eq!(test_text, text_out);
673
674    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676    assert_eq!(term.ring_srow(), 0);
677    assert_eq!(term.ring_erow(), ring_rows - 1);
678    assert_eq!(
679        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680        term.display_rows()
681    );
682    assert_eq!(
683        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684        term.history_rows()
685    );
686
687    term.append(&format!(
688        "\n\nScreen has {} rows of {} columns.\n",
689        term.display_rows(),
690        term.display_columns()
691    ));
692
693    term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
Source

pub fn ring_rows(&self) -> i32

Return the number of rows in the ring buffer.

Examples found in repository?
examples/terminal.rs (line 572)
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555    let sel_len = term.selection_text_len();
556    let sel = term.selection_text();
557
558    term.take_focus().unwrap();
559    term.reset_terminal();
560    // Test the Utf8Char primitive
561    let uc = Utf8Char::new(b'Q');
562    let uc1 = uc.text_utf8();
563    assert_eq!(&uc1, b"Q");
564    assert_eq!(&uc.attrib(), &Attrib::Normal);
565    assert_eq!(
566        &uc.charflags(),
567        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568    );
569    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570    assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572    let ring_rows = term.ring_rows();
573
574    term.take_focus().unwrap();
575    term.clear_history();
576    assert_eq!(term.history_use(), 0);
577
578    // Subtract row numbers, modulo `rows`
579    fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580        match a - b {
581            n if n < 0 => n + rows,
582            n => n,
583        }
584    }
585    // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587    assert!(term.disp_srow() >= 0);
588    assert!(term.disp_erow() >= 0);
589    assert!(term.hist_srow() >= 0);
590    assert!(term.hist_erow() >= 0);
591    assert!(term.offset() >= 0);
592    assert!(term.disp_srow() <= ring_rows);
593    assert!(term.disp_erow() <= ring_rows);
594    assert!(term.hist_srow() <= ring_rows);
595    assert!(term.hist_erow() <= ring_rows);
596    assert!(term.offset() <= ring_rows);
597
598    assert_eq!(term.ring_srow(), 0);
599    assert_eq!(term.ring_erow(), ring_rows - 1);
600    assert_eq!(
601        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602        term.display_rows()
603    );
604    assert_eq!(
605        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606        term.history_rows()
607    );
608
609    assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610    assert_eq!(term.ring_srow(), 0);
611
612    /// Local function to read back all rows from the display into a long string.
613    /// Does not include scrollback history.
614    /// Trims trailing blanks on each line
615    fn read_disp(term: &Terminal) -> String {
616        let rows = term.display_rows();
617        let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618        for row in 0..rows {
619            let r = term.u8c_disp_row(row).trim();
620            // Iterate through a row, accumulating [u8]
621            for c in r.iter() {
622                // Note: Sometimes utf-8 length is > 1
623                text.extend_from_slice(c.text_utf8());
624            }
625            text.extend_from_slice(b"\n");
626        }
627        // Return the result as a string
628        std::str::from_utf8(&text).unwrap().to_string()
629    }
630
631    term.clear();
632    term.append("Top line  ↑ (up-arrow)");
633    term.set_text_attrib(Attrib::Underline);
634    term.append("  ");
635    term.set_text_attrib(Attrib::Normal);
636    term.append("  \n");
637    let mut text_out = read_disp(term);
638    // Trim trailing empty lines
639    text_out = text_out.trim_end_matches(&"\n").to_string();
640    // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642    assert_eq!(text_out, "Top line  ↑ (up-arrow)  ");
643    let r = term.u8c_disp_row(0);
644    assert_eq!(r.col(0).text_utf8(), b"T");
645    assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646    assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647    let r = term.u8c_disp_row(1);
648    assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649    assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651    // Clear the screen again, then append test text, then read it back and compare
652    let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656            Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659    term.clear_history();
660    term.clear();
661    let bg_save = term.text_bg_color();
662    let fg_save = term.text_fg_color();
663    term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664    term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665    term.append(test_text);
666    term.set_text_bg_color(bg_save);
667    term.set_text_fg_color(fg_save);
668
669    let mut text_out = read_disp(term);
670    // Trim trailing empty lines
671    text_out = text_out.trim_end_matches(&"\n").to_string();
672    assert_eq!(test_text, text_out);
673
674    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676    assert_eq!(term.ring_srow(), 0);
677    assert_eq!(term.ring_erow(), ring_rows - 1);
678    assert_eq!(
679        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680        term.display_rows()
681    );
682    assert_eq!(
683        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684        term.history_rows()
685    );
686
687    term.append(&format!(
688        "\n\nScreen has {} rows of {} columns.\n",
689        term.display_rows(),
690        term.display_columns()
691    ));
692
693    term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
Source

pub fn u8c_cursor(&self) -> Utf8Char

Return the Utf8Char for character under cursor.

Examples found in repository?
examples/terminal.rs (line 854)
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700    term.take_focus().unwrap();
701
702    // Test the attr_fg_color and attr_bg_color methods.
703    // Put a single character 'A' into the buffer and check it
704    term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705    term.set_text_bg_color(Color::TransparentBg);
706    term.set_text_fg_color(Color::XtermWhite);
707    term.append("A");
708    let r = &term.u8c_disp_row(0);
709    let uc = r.col(0);
710    assert_eq!(uc.text_utf8(), b"A");
711    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712    assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713    assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714    assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715    assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717    // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718    term.clear();
719    term.set_text_fg_color_xterm(XtermColor::White);
720    term.set_text_bg_color_xterm(XtermColor::Black);
721    assert_eq!(term.text_attrib(), Attrib::Normal);
722
723    assert!(term.ansi());
724    term.append("B\x1b[32mC\x1b[1mD\n");
725
726    let r = &term.u8c_disp_row(0);
727    let uc = r.col(0);
728    assert_eq!(uc.text_utf8(), b"B");
729    assert!(uc.is_char(b'B'));
730    assert!(!uc.is_char(b'A'));
731    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735    assert_eq!(
736        &uc.charflags(),
737        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738    );
739
740    let uc = r.col(1);
741    assert_eq!(uc.text_utf8(), b"C");
742    assert!(uc.is_char(b'C'));
743    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747    assert_eq!(
748        &uc.charflags(),
749        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750    );
751
752    let uc = r.col(2);
753    assert_eq!(uc.text_utf8(), b"D");
754    assert!(uc.is_char(b'D'));
755    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757    assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759    assert_eq!(
760        &uc.attr_fgcolor(Some(term)),
761        &Color::from_rgb(0x20, 0xf0, 0x20)
762    );
763    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764    assert_eq!(
765        &uc.charflags(),
766        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767    );
768
769    // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770    term.clear();
771    term.set_text_fg_color_xterm(XtermColor::White);
772    term.set_text_bg_color_xterm(XtermColor::Black);
773    term.set_text_attrib(Attrib::Normal);
774    assert_eq!(term.text_attrib(), Attrib::Normal);
775
776    assert!(term.ansi());
777    term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779    let r = &term.u8c_disp_row(0);
780    let uc = r.col(0);
781    assert_eq!(uc.text_utf8(), b"B");
782    assert!(uc.is_char(b'B'));
783    assert!(!uc.is_char(b'A'));
784    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788    assert_eq!(
789        &uc.charflags(),
790        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791    );
792
793    let uc = r.col(1);
794    assert_eq!(uc.text_utf8(), b"C");
795    assert!(uc.is_char(b'C'));
796    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800    assert_eq!(
801        &uc.charflags(),
802        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803    );
804
805    let uc = r.col(2);
806    assert_eq!(uc.text_utf8(), b"D");
807    assert!(uc.is_char(b'D'));
808    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812    assert_eq!(
813        &uc.charflags(),
814        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815    );
816
817    let uc = r.col(3);
818    assert_eq!(uc.text_utf8(), b"E");
819    assert!(uc.is_char(b'E'));
820    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822    assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823    assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824    assert_eq!(
825        &uc.charflags(),
826        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827    );
828
829    // Test some miscellaneous Utf8 constants
830    assert_eq!(uc.length(), 1);
831    assert_eq!(uc.max_utf8(), 4);
832    assert_eq!(uc.pwidth(), 9.0);
833    assert_eq!(uc.pwidth_int(), 9);
834
835    term.set_text_fg_color_xterm(XtermColor::White);
836    term.set_text_bg_color_xterm(XtermColor::Black);
837    term.clear();
838    term.set_text_attrib(Attrib::Normal);
839
840    // Mouse selection functions
841    term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842    term.clear_mouse_selection();
843    assert_eq!(term.get_selection(), None);
844
845    // Play with cursor position
846    term.append("0123456789\n"); // Set up test pattern
847    term.append("ABCDEFGHIJ\n");
848    term.append("abcdefghij\n");
849
850    term.set_cursor_row(1);
851    assert_eq!(term.cursor_row(), 1);
852    term.set_cursor_col(1);
853    assert_eq!(term.cursor_col(), 1);
854    assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856    term.append("----"); // Overwrites text at cursor and moves cursor forward
857    assert_eq!(term.cursor_row(), 1);
858    assert_eq!(term.cursor_col(), 5);
859    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860    term.set_cursor_col(1);
861    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863    term.cursor_up(1, false);
864    assert_eq!(term.cursor_row(), 0);
865    assert_eq!(term.cursor_col(), 1);
866    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868    // Hit top of screen, so nothing happens
869    term.cursor_up(1, false);
870    assert_eq!(term.cursor_row(), 0);
871    assert_eq!(term.cursor_col(), 1);
872    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874    // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875    term.cursor_up(1, true);
876    assert_eq!(term.cursor_row(), 0);
877    assert_eq!(term.cursor_col(), 1);
878    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880    // Go back down to the overwritten text
881    term.cursor_down(2, false);
882    assert_eq!(term.cursor_row(), 2);
883    assert_eq!(term.cursor_col(), 1);
884    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886    // Go right past the overwritten text
887    term.cursor_right(4, false);
888    assert_eq!(term.cursor_row(), 2);
889    assert_eq!(term.cursor_col(), 5);
890    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892    // Go left to the end of the overwritten text
893    term.cursor_left(1);
894    assert_eq!(term.cursor_row(), 2);
895    assert_eq!(term.cursor_col(), 4);
896    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898    // Scroll back down, removing the blank line at the top.
899    // Cursor stays in place, the text moves under it.
900    term.scroll(1);
901    assert_eq!(term.cursor_row(), 2);
902    assert_eq!(term.cursor_col(), 4);
903    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905    // Clear from here to end-of-line
906    term.clear_eol();
907    assert_eq!(term.cursor_row(), 2);
908    assert_eq!(term.cursor_col(), 4);
909    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911    // Now clear from here to start-of-line. Cursor does not move.
912    term.clear_sol();
913    assert_eq!(term.cursor_row(), 2);
914    assert_eq!(term.cursor_col(), 4);
915    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916    term.cursor_left(1);
917    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918    term.set_cursor_col(0);
919    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921    // Clear some lines
922    term.clear_line(1);
923    assert_eq!(term.cursor_row(), 2);
924    assert_eq!(term.cursor_col(), 0);
925    term.set_cursor_row(1);
926    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927    term.set_cursor_row(3);
928    term.clear_cur_line();
929    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930    assert_eq!(term.cursor_row(), 3);
931    assert_eq!(term.cursor_col(), 0);
932
933    term.append("Two lines above are intentionally left blank.\n");
934    assert_eq!(term.cursor_row(), 4);
935    assert_eq!(term.cursor_col(), 0);
936
937    // Set up the test pattern again, then play with insert/delete
938    term.append("0123456789\n");
939    term.append("ABCDEFGHIJ\n");
940    term.append("abcdefghij\n");
941    assert_eq!(term.cursor_row(), 7);
942
943    term.set_cursor_row(4);
944    term.set_cursor_col(4);
945    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947    term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949    term.cursor_right(5, false);
950    assert_eq!(term.cursor_col(), 9);
951    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953    // Insert two blank rows above cursor. Cursor stays put.
954    term.insert_rows(2);
955    assert_eq!(term.cursor_row(), 4);
956    assert_eq!(term.cursor_col(), 9);
957    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958    term.cursor_down(2, false); // Go down to find our text again
959    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961    // Go back to the beginning of the inserted 'x' characters and delete them.
962    term.cursor_left(5);
963    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964    term.delete_cur_chars(5);
965    assert_eq!(term.cursor_row(), 6);
966    assert_eq!(term.cursor_col(), 4);
967    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969    term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970    term.cursor_down(1, false);
971    term.cursor_left(2);
972    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974    term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975    assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976    term.cursor_up(1, false);
977    term.delete_rows(2); // Delete remains of test pattern
978
979    term.set_text_attrib(Attrib::Bold);
980    term.insert_char_eol('-', 3, 15, 20);
981    term.set_cursor_row(3);
982    term.set_cursor_col(15);
983    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984    assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986    term.set_text_attrib(Attrib::Italic);
987    term.append(" and all lines below");
988    term.set_text_attrib(Attrib::Normal);
989    term.cursor_down(1, false);
990
991    let mut hsb = term.hscrollbar();
992    let mut sb = term.scrollbar();
993    hsb.set_value(100.0);
994    assert_eq!(hsb.value(), 100.0);
995    sb.set_value(50.0);
996    assert_eq!(sb.value(), 50.0);
997    hsb.set_value(0.0);
998    assert_eq!(hsb.value(), 0.0);
999    sb.set_value(0.0);
1000    assert_eq!(sb.value(), 0.0);
1001}
Source

pub fn u8c_disp_row(&self, drow: i32) -> BuffRow<'_>

Return u8c for beginning of row drow of the display. This is a low-level “protected” function of the fltk library

Examples found in repository?
examples/terminal.rs (line 619)
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555    let sel_len = term.selection_text_len();
556    let sel = term.selection_text();
557
558    term.take_focus().unwrap();
559    term.reset_terminal();
560    // Test the Utf8Char primitive
561    let uc = Utf8Char::new(b'Q');
562    let uc1 = uc.text_utf8();
563    assert_eq!(&uc1, b"Q");
564    assert_eq!(&uc.attrib(), &Attrib::Normal);
565    assert_eq!(
566        &uc.charflags(),
567        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568    );
569    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570    assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572    let ring_rows = term.ring_rows();
573
574    term.take_focus().unwrap();
575    term.clear_history();
576    assert_eq!(term.history_use(), 0);
577
578    // Subtract row numbers, modulo `rows`
579    fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580        match a - b {
581            n if n < 0 => n + rows,
582            n => n,
583        }
584    }
585    // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587    assert!(term.disp_srow() >= 0);
588    assert!(term.disp_erow() >= 0);
589    assert!(term.hist_srow() >= 0);
590    assert!(term.hist_erow() >= 0);
591    assert!(term.offset() >= 0);
592    assert!(term.disp_srow() <= ring_rows);
593    assert!(term.disp_erow() <= ring_rows);
594    assert!(term.hist_srow() <= ring_rows);
595    assert!(term.hist_erow() <= ring_rows);
596    assert!(term.offset() <= ring_rows);
597
598    assert_eq!(term.ring_srow(), 0);
599    assert_eq!(term.ring_erow(), ring_rows - 1);
600    assert_eq!(
601        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602        term.display_rows()
603    );
604    assert_eq!(
605        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606        term.history_rows()
607    );
608
609    assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610    assert_eq!(term.ring_srow(), 0);
611
612    /// Local function to read back all rows from the display into a long string.
613    /// Does not include scrollback history.
614    /// Trims trailing blanks on each line
615    fn read_disp(term: &Terminal) -> String {
616        let rows = term.display_rows();
617        let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618        for row in 0..rows {
619            let r = term.u8c_disp_row(row).trim();
620            // Iterate through a row, accumulating [u8]
621            for c in r.iter() {
622                // Note: Sometimes utf-8 length is > 1
623                text.extend_from_slice(c.text_utf8());
624            }
625            text.extend_from_slice(b"\n");
626        }
627        // Return the result as a string
628        std::str::from_utf8(&text).unwrap().to_string()
629    }
630
631    term.clear();
632    term.append("Top line  ↑ (up-arrow)");
633    term.set_text_attrib(Attrib::Underline);
634    term.append("  ");
635    term.set_text_attrib(Attrib::Normal);
636    term.append("  \n");
637    let mut text_out = read_disp(term);
638    // Trim trailing empty lines
639    text_out = text_out.trim_end_matches(&"\n").to_string();
640    // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642    assert_eq!(text_out, "Top line  ↑ (up-arrow)  ");
643    let r = term.u8c_disp_row(0);
644    assert_eq!(r.col(0).text_utf8(), b"T");
645    assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646    assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647    let r = term.u8c_disp_row(1);
648    assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649    assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651    // Clear the screen again, then append test text, then read it back and compare
652    let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656            Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659    term.clear_history();
660    term.clear();
661    let bg_save = term.text_bg_color();
662    let fg_save = term.text_fg_color();
663    term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664    term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665    term.append(test_text);
666    term.set_text_bg_color(bg_save);
667    term.set_text_fg_color(fg_save);
668
669    let mut text_out = read_disp(term);
670    // Trim trailing empty lines
671    text_out = text_out.trim_end_matches(&"\n").to_string();
672    assert_eq!(test_text, text_out);
673
674    assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676    assert_eq!(term.ring_srow(), 0);
677    assert_eq!(term.ring_erow(), ring_rows - 1);
678    assert_eq!(
679        row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680        term.display_rows()
681    );
682    assert_eq!(
683        row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684        term.history_rows()
685    );
686
687    term.append(&format!(
688        "\n\nScreen has {} rows of {} columns.\n",
689        term.display_rows(),
690        term.display_columns()
691    ));
692
693    term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
695
696//--------------------------------------------------------------------------------------
697/// Yet another set of tests for misc cursor functions and other stuff
698/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700    term.take_focus().unwrap();
701
702    // Test the attr_fg_color and attr_bg_color methods.
703    // Put a single character 'A' into the buffer and check it
704    term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705    term.set_text_bg_color(Color::TransparentBg);
706    term.set_text_fg_color(Color::XtermWhite);
707    term.append("A");
708    let r = &term.u8c_disp_row(0);
709    let uc = r.col(0);
710    assert_eq!(uc.text_utf8(), b"A");
711    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712    assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713    assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714    assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715    assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717    // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718    term.clear();
719    term.set_text_fg_color_xterm(XtermColor::White);
720    term.set_text_bg_color_xterm(XtermColor::Black);
721    assert_eq!(term.text_attrib(), Attrib::Normal);
722
723    assert!(term.ansi());
724    term.append("B\x1b[32mC\x1b[1mD\n");
725
726    let r = &term.u8c_disp_row(0);
727    let uc = r.col(0);
728    assert_eq!(uc.text_utf8(), b"B");
729    assert!(uc.is_char(b'B'));
730    assert!(!uc.is_char(b'A'));
731    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735    assert_eq!(
736        &uc.charflags(),
737        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738    );
739
740    let uc = r.col(1);
741    assert_eq!(uc.text_utf8(), b"C");
742    assert!(uc.is_char(b'C'));
743    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747    assert_eq!(
748        &uc.charflags(),
749        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750    );
751
752    let uc = r.col(2);
753    assert_eq!(uc.text_utf8(), b"D");
754    assert!(uc.is_char(b'D'));
755    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757    assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759    assert_eq!(
760        &uc.attr_fgcolor(Some(term)),
761        &Color::from_rgb(0x20, 0xf0, 0x20)
762    );
763    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764    assert_eq!(
765        &uc.charflags(),
766        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767    );
768
769    // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770    term.clear();
771    term.set_text_fg_color_xterm(XtermColor::White);
772    term.set_text_bg_color_xterm(XtermColor::Black);
773    term.set_text_attrib(Attrib::Normal);
774    assert_eq!(term.text_attrib(), Attrib::Normal);
775
776    assert!(term.ansi());
777    term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779    let r = &term.u8c_disp_row(0);
780    let uc = r.col(0);
781    assert_eq!(uc.text_utf8(), b"B");
782    assert!(uc.is_char(b'B'));
783    assert!(!uc.is_char(b'A'));
784    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788    assert_eq!(
789        &uc.charflags(),
790        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791    );
792
793    let uc = r.col(1);
794    assert_eq!(uc.text_utf8(), b"C");
795    assert!(uc.is_char(b'C'));
796    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800    assert_eq!(
801        &uc.charflags(),
802        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803    );
804
805    let uc = r.col(2);
806    assert_eq!(uc.text_utf8(), b"D");
807    assert!(uc.is_char(b'D'));
808    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812    assert_eq!(
813        &uc.charflags(),
814        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815    );
816
817    let uc = r.col(3);
818    assert_eq!(uc.text_utf8(), b"E");
819    assert!(uc.is_char(b'E'));
820    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822    assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823    assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824    assert_eq!(
825        &uc.charflags(),
826        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827    );
828
829    // Test some miscellaneous Utf8 constants
830    assert_eq!(uc.length(), 1);
831    assert_eq!(uc.max_utf8(), 4);
832    assert_eq!(uc.pwidth(), 9.0);
833    assert_eq!(uc.pwidth_int(), 9);
834
835    term.set_text_fg_color_xterm(XtermColor::White);
836    term.set_text_bg_color_xterm(XtermColor::Black);
837    term.clear();
838    term.set_text_attrib(Attrib::Normal);
839
840    // Mouse selection functions
841    term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842    term.clear_mouse_selection();
843    assert_eq!(term.get_selection(), None);
844
845    // Play with cursor position
846    term.append("0123456789\n"); // Set up test pattern
847    term.append("ABCDEFGHIJ\n");
848    term.append("abcdefghij\n");
849
850    term.set_cursor_row(1);
851    assert_eq!(term.cursor_row(), 1);
852    term.set_cursor_col(1);
853    assert_eq!(term.cursor_col(), 1);
854    assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856    term.append("----"); // Overwrites text at cursor and moves cursor forward
857    assert_eq!(term.cursor_row(), 1);
858    assert_eq!(term.cursor_col(), 5);
859    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860    term.set_cursor_col(1);
861    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863    term.cursor_up(1, false);
864    assert_eq!(term.cursor_row(), 0);
865    assert_eq!(term.cursor_col(), 1);
866    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868    // Hit top of screen, so nothing happens
869    term.cursor_up(1, false);
870    assert_eq!(term.cursor_row(), 0);
871    assert_eq!(term.cursor_col(), 1);
872    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874    // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875    term.cursor_up(1, true);
876    assert_eq!(term.cursor_row(), 0);
877    assert_eq!(term.cursor_col(), 1);
878    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880    // Go back down to the overwritten text
881    term.cursor_down(2, false);
882    assert_eq!(term.cursor_row(), 2);
883    assert_eq!(term.cursor_col(), 1);
884    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886    // Go right past the overwritten text
887    term.cursor_right(4, false);
888    assert_eq!(term.cursor_row(), 2);
889    assert_eq!(term.cursor_col(), 5);
890    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892    // Go left to the end of the overwritten text
893    term.cursor_left(1);
894    assert_eq!(term.cursor_row(), 2);
895    assert_eq!(term.cursor_col(), 4);
896    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898    // Scroll back down, removing the blank line at the top.
899    // Cursor stays in place, the text moves under it.
900    term.scroll(1);
901    assert_eq!(term.cursor_row(), 2);
902    assert_eq!(term.cursor_col(), 4);
903    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905    // Clear from here to end-of-line
906    term.clear_eol();
907    assert_eq!(term.cursor_row(), 2);
908    assert_eq!(term.cursor_col(), 4);
909    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911    // Now clear from here to start-of-line. Cursor does not move.
912    term.clear_sol();
913    assert_eq!(term.cursor_row(), 2);
914    assert_eq!(term.cursor_col(), 4);
915    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916    term.cursor_left(1);
917    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918    term.set_cursor_col(0);
919    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921    // Clear some lines
922    term.clear_line(1);
923    assert_eq!(term.cursor_row(), 2);
924    assert_eq!(term.cursor_col(), 0);
925    term.set_cursor_row(1);
926    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927    term.set_cursor_row(3);
928    term.clear_cur_line();
929    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930    assert_eq!(term.cursor_row(), 3);
931    assert_eq!(term.cursor_col(), 0);
932
933    term.append("Two lines above are intentionally left blank.\n");
934    assert_eq!(term.cursor_row(), 4);
935    assert_eq!(term.cursor_col(), 0);
936
937    // Set up the test pattern again, then play with insert/delete
938    term.append("0123456789\n");
939    term.append("ABCDEFGHIJ\n");
940    term.append("abcdefghij\n");
941    assert_eq!(term.cursor_row(), 7);
942
943    term.set_cursor_row(4);
944    term.set_cursor_col(4);
945    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947    term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949    term.cursor_right(5, false);
950    assert_eq!(term.cursor_col(), 9);
951    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953    // Insert two blank rows above cursor. Cursor stays put.
954    term.insert_rows(2);
955    assert_eq!(term.cursor_row(), 4);
956    assert_eq!(term.cursor_col(), 9);
957    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958    term.cursor_down(2, false); // Go down to find our text again
959    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961    // Go back to the beginning of the inserted 'x' characters and delete them.
962    term.cursor_left(5);
963    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964    term.delete_cur_chars(5);
965    assert_eq!(term.cursor_row(), 6);
966    assert_eq!(term.cursor_col(), 4);
967    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969    term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970    term.cursor_down(1, false);
971    term.cursor_left(2);
972    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974    term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975    assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976    term.cursor_up(1, false);
977    term.delete_rows(2); // Delete remains of test pattern
978
979    term.set_text_attrib(Attrib::Bold);
980    term.insert_char_eol('-', 3, 15, 20);
981    term.set_cursor_row(3);
982    term.set_cursor_col(15);
983    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984    assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986    term.set_text_attrib(Attrib::Italic);
987    term.append(" and all lines below");
988    term.set_text_attrib(Attrib::Normal);
989    term.cursor_down(1, false);
990
991    let mut hsb = term.hscrollbar();
992    let mut sb = term.scrollbar();
993    hsb.set_value(100.0);
994    assert_eq!(hsb.value(), 100.0);
995    sb.set_value(50.0);
996    assert_eq!(sb.value(), 50.0);
997    hsb.set_value(0.0);
998    assert_eq!(hsb.value(), 0.0);
999    sb.set_value(0.0);
1000    assert_eq!(sb.value(), 0.0);
1001}
Source

pub fn u8c_hist_row(&self, hrow: i32) -> BuffRow<'_>

Return u8c for beginning of row hrow inside the scrollback history. This is a low-level “protected” function of the fltk library

Source

pub fn u8c_hist_use_row(&self, hurow: i32) -> BuffRow<'_>

Return u8c for beginning of row hurow inside the ‘in use’ part of the\n scrollback history. This is a low-level “protected” function of the fltk library

Source

pub fn u8c_ring_row(&self, grow: i32) -> BuffRow<'_>

Return u8c for beginning of row grow in the ring buffer. This is a low-level “protected” function of the fltk library

Trait Implementations§

Source§

impl Clone for Terminal

Source§

fn clone(&self) -> Terminal

Returns a copy of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Terminal

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for Terminal

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl GroupExt for Terminal

Source§

fn begin(&self)

Begins a group, used for widgets implementing the group trait
Source§

fn end(&self)

Ends a group, used for widgets implementing the group trait
Source§

fn clear(&mut self)

Clear a group from all widgets
Source§

fn children(&self) -> i32

Return the number of children in a group
Source§

fn child(&self, idx: i32) -> Option<Widget>

Return child widget by index
Source§

fn find<W: WidgetExt>(&self, widget: &W) -> i32

Find a widget within a group and return its index
Source§

fn add<W: WidgetExt>(&mut self, widget: &W)

Add a widget to a group
Source§

fn insert<W: WidgetExt>(&mut self, widget: &W, index: i32)

Insert a widget to a group at a certain index
Source§

fn remove<W: WidgetExt>(&mut self, widget: &W)

Remove a widget from a group, but does not delete it
Source§

fn remove_by_index(&mut self, idx: i32)

Remove a child widget by its index
Source§

fn resizable<W: WidgetExt>(&self, widget: &W)

The resizable widget defines both the resizing frame and the resizing behavior of the group and its children.
Source§

fn make_resizable(&mut self, val: bool)

Make the group itself resizable, should be called before the widget is shown
Source§

fn add_resizable<W: WidgetExt>(&mut self, widget: &W)

Adds a widget to the group and makes it the resizable widget
Source§

fn set_clip_children(&mut self, flag: bool)

Clips children outside the group boundaries
Source§

fn clip_children(&self) -> bool

Get whether clip_children is set
Source§

fn draw_child<W: WidgetExt>(&self, w: &mut W)

Draw a child widget, the call should be in a WidgetBase::draw method
Source§

fn update_child<W: WidgetExt>(&self, w: &mut W)

Update a child widget, the call should be in a WidgetBase::draw method
Source§

fn draw_outside_label<W: WidgetExt>(&self, w: &mut W)

Draw the outside label, the call should be in a WidgetBase::draw method
Source§

fn draw_children(&mut self)

Draw children, the call should be in a WidgetBase::draw method
Source§

fn init_sizes(&mut self)

Resets the internal array of widget sizes and positions
Source§

fn bounds(&self) -> Vec<(i32, i32, i32, i32)>

Get the bounds of all children widgets (left, upper, right, bottom)
Source§

unsafe fn as_group(&self) -> Group

Converts a widget implementing GroupExt into a Group widget Read more
Source§

impl IntoIterator for Terminal

Source§

type Item = Widget

The type of the elements being iterated over.
Source§

type IntoIter = IntoIter<<Terminal as IntoIterator>::Item>

Which kind of iterator are we turning this into?
Source§

fn into_iter(self) -> Self::IntoIter

Creates an iterator from a value. Read more
Source§

impl PartialEq for Terminal

Source§

fn eq(&self, other: &Self) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl WidgetBase for Terminal

Source§

fn delete(wid: Self)

Deletes widgets and their children.
Source§

unsafe fn from_widget_ptr(ptr: *mut Fl_Widget) -> Self

transforms a widget pointer to a Widget, for internal use Read more
Source§

unsafe fn from_widget<W: WidgetExt>(w: W) -> Self

Get a widget from base widget Read more
Source§

fn handle<F: FnMut(&mut Self, Event) -> bool + 'static>(&mut self, cb: F)

Set a custom handler, where events are managed manually, akin to Fl_Widget::handle(int). Handled or ignored events should return true, unhandled events should return false. takes the widget as a closure argument. The ability to handle an event might depend on handling other events, as explained here
Source§

fn draw<F: FnMut(&mut Self) + 'static>(&mut self, cb: F)

Set a custom draw method. takes the widget as a closure argument. macOS requires that WidgetBase::draw actually calls drawing functions
Source§

fn resize_callback<F: FnMut(&mut Self, i32, i32, i32, i32) + 'static>( &mut self, cb: F, )

Perform a callback on resize. Avoid resizing the parent or the same widget to avoid infinite recursion
Source§

unsafe fn assume_derived(&mut self)

Makes the widget derived Read more
Source§

fn from_dyn_widget<W: WidgetExt>(w: &W) -> Option<Self>

Cast a type-erased widget back to its original widget
Source§

fn from_dyn_widget_ptr(w: *mut Fl_Widget) -> Option<Self>

Cast a type-erased widget pointer back to its original widget
Source§

impl WidgetExt for Terminal

Source§

fn set_label(&mut self, title: &str)

Sets the widget’s label. labels support special symbols preceded by an @ sign. and for the associated formatting.
Source§

fn unset_label(&mut self)

Unset a widget’s label
Source§

fn redraw(&mut self)

Redraws a widget, necessary for resizing and changing positions
Source§

fn show(&mut self)

Shows the widget
Source§

fn hide(&mut self)

Hides the widget
Source§

fn x(&self) -> i32

Returns the x coordinate of the widget
Source§

fn y(&self) -> i32

Returns the y coordinate of the widget
Source§

fn w(&self) -> i32

Returns the width of the widget
Source§

fn h(&self) -> i32

Returns the height of the widget
Source§

fn label(&self) -> Option<String>

Returns the label of the widget
Source§

fn measure_label(&self) -> (i32, i32)

Measures the label’s width and height
Source§

fn as_widget_ptr(&self) -> *mut Fl_Widget

transforms a widget to a base Fl_Widget, for internal use
Source§

fn activate(&mut self)

Activates the widget
Source§

fn deactivate(&mut self)

Deactivates the widget
Source§

fn redraw_label(&mut self)

Redraws the label of the widget
Source§

fn resize(&mut self, x: i32, y: i32, width: i32, height: i32)

Resizes and/or moves the widget, takes x, y, width and height
Source§

fn widget_resize(&mut self, x: i32, y: i32, width: i32, height: i32)

Does a simple resize ignoring class-specific resize functionality
Source§

fn tooltip(&self) -> Option<String>

Returns the tooltip text
Source§

fn set_tooltip(&mut self, txt: &str)

Sets the tooltip text
Source§

fn color(&self) -> Color

Returns the widget color
Source§

fn set_color(&mut self, color: Color)

Sets the widget’s color
Source§

fn label_color(&self) -> Color

Returns the widget label’s color
Source§

fn set_label_color(&mut self, color: Color)

Sets the widget label’s color
Source§

fn label_font(&self) -> Font

Returns the widget label’s font
Source§

fn set_label_font(&mut self, font: Font)

Sets the widget label’s font
Source§

fn label_size(&self) -> i32

Returns the widget label’s size
Source§

fn set_label_size(&mut self, sz: i32)

Sets the widget label’s size
Source§

fn label_type(&self) -> LabelType

Returns the widget label’s type
Source§

fn set_label_type(&mut self, typ: LabelType)

Sets the widget label’s type
Source§

fn frame(&self) -> FrameType

Returns the widget’s frame type
Source§

fn set_frame(&mut self, typ: FrameType)

Sets the widget’s frame type
Source§

fn changed(&self) -> bool

Returns whether the widget was changed
Source§

fn set_changed(&mut self)

Mark the widget as changed
Source§

fn clear_changed(&mut self)

Clears the changed status of the widget
Source§

fn align(&self) -> Align

Returns the alignment of the widget
Source§

fn set_align(&mut self, align: Align)

Sets the alignment of the widget
Source§

fn set_when(&mut self, trigger: When)

Sets the default callback trigger for a widget, equivalent to when()
Source§

fn when(&self) -> When

Return the callback trigger, equivalent to when()
Source§

fn parent(&self) -> Option<Group>

Returns the parent of the widget
Source§

fn selection_color(&self) -> Color

Gets the selection color of the widget
Source§

fn set_selection_color(&mut self, color: Color)

Sets the selection color of the widget
Source§

fn do_callback(&mut self)

Runs the already registered callback
Source§

fn window(&self) -> Option<Box<dyn WindowExt>>

Returns the direct window holding the widget
Source§

fn top_window(&self) -> Option<Box<dyn WindowExt>>

Returns the topmost window holding the widget
Source§

fn takes_events(&self) -> bool

Checks whether a widget is capable of taking events
Source§

fn take_focus(&mut self) -> Result<(), FltkError>

Make the widget take focus Read more
Source§

fn set_visible_focus(&mut self)

Set the widget to have visible focus
Source§

fn clear_visible_focus(&mut self)

Clear visible focus
Source§

fn visible_focus(&mut self, v: bool)

Set the visible focus using a flag
Source§

fn has_visible_focus(&self) -> bool

Return whether the widget has visible focus
Source§

fn has_focus(&self) -> bool

Return whether the widget has focus
Source§

fn was_deleted(&self) -> bool

Check if a widget was deleted
Source§

fn damage(&self) -> bool

Return whether the widget was damaged
Source§

fn set_damage(&mut self, flag: bool)

Signal the widget as damaged and it should be redrawn in the next event loop cycle
Source§

fn damage_type(&self) -> Damage

Return the damage mask
Source§

fn set_damage_type(&mut self, mask: Damage)

Signal the type of damage a widget received
Source§

fn set_damage_area(&mut self, mask: Damage, x: i32, y: i32, w: i32, h: i32)

Signal damage for an area inside the widget
Source§

fn clear_damage(&mut self)

Clear the damaged flag
Source§

fn as_window(&self) -> Option<Box<dyn WindowExt>>

Return the widget as a window if it’s a window
Source§

fn as_group(&self) -> Option<Group>

Return the widget as a group widget if it’s a group widget
Source§

fn inside<W: WidgetExt>(&self, wid: &W) -> bool

Checks whether the self widget is inside another widget
Source§

fn get_type<T: WidgetType>(&self) -> T

Returns the widget type when applicable
Source§

fn set_type<T: WidgetType>(&mut self, typ: T)

Sets the widget type
Source§

fn set_image<I: ImageExt>(&mut self, image: Option<I>)

Sets the image of the widget
Source§

fn set_image_scaled<I: ImageExt>(&mut self, image: Option<I>)

Sets the image of the widget scaled to the widget’s size
Source§

fn image(&self) -> Option<Box<dyn ImageExt>>

Gets the image associated with the widget
Source§

fn set_deimage<I: ImageExt>(&mut self, image: Option<I>)

Sets the deactivated image of the widget
Source§

fn set_deimage_scaled<I: ImageExt>(&mut self, image: Option<I>)

Sets the deactivated image of the widget scaled to the widget’s size
Source§

fn deimage(&self) -> Option<Box<dyn ImageExt>>

Gets the deactivated image associated with the widget
Source§

fn set_callback<F: FnMut(&mut Self) + 'static>(&mut self, cb: F)

Sets the callback when the widget is triggered (clicks for example) takes the widget as a closure argument
Source§

fn emit<T: 'static + Clone + Send + Sync>(&mut self, sender: Sender<T>, msg: T)

Emits a message on callback using a sender
Source§

unsafe fn as_widget<W: WidgetBase>(&self) -> W

Upcast a WidgetExt to some widget type Read more
Source§

fn visible(&self) -> bool

Returns whether a widget is visible
Source§

fn visible_r(&self) -> bool

Returns whether a widget or any of its parents are visible (recursively)
Source§

fn is_same<W: WidgetExt>(&self, other: &W) -> bool

Return whether two widgets object point to the same widget
Source§

fn active(&self) -> bool

Returns whether a widget is active
Source§

fn active_r(&self) -> bool

Returns whether a widget or any of its parents are active (recursively)
Source§

fn handle_event(&mut self, event: Event) -> bool

Handle a specific event
Source§

fn is_derived(&self) -> bool

Check whether a widget is derived
Source§

fn as_base_widget(&self) -> Widget
where Self: Sized,

Upcast a WidgetExt to a Widget
Source§

impl WidgetProps for Terminal

Source§

fn with_pos(self, x: i32, y: i32) -> Self

Initialize to position x, y

Source§

fn with_size(self, width: i32, height: i32) -> Self

Initialize to size width, height

Source§

fn with_label(self, title: &str) -> Self

Initialize with a label

Source§

fn with_align(self, align: Align) -> Self

Initialize with alignment

Source§

fn with_type<T: WidgetType>(self, typ: T) -> Self

Initialize with type

Source§

fn below_of<W: WidgetExt>(self, wid: &W, padding: i32) -> Self

Initialize at bottom of another widget

Source§

fn above_of<W: WidgetExt>(self, wid: &W, padding: i32) -> Self

Initialize above of another widget

Source§

fn right_of<W: WidgetExt>(self, wid: &W, padding: i32) -> Self

Initialize right of another widget

Source§

fn left_of<W: WidgetExt>(self, wid: &W, padding: i32) -> Self

Initialize left of another widget

Source§

fn center_of<W: WidgetExt>(self, w: &W) -> Self

Initialize center of another widget

Source§

fn center_x<W: WidgetExt>(self, w: &W) -> Self

Initialize center of another widget on the x axis

Source§

fn center_y<W: WidgetExt>(self, w: &W) -> Self

Initialize center of another widget on the y axis

Source§

fn center_of_parent(self) -> Self

Initialize center of parent

Source§

fn size_of<W: WidgetExt>(self, w: &W) -> Self

Initialize to the size of another widget

Source§

fn size_of_parent(self) -> Self

Initialize to the size of the parent

Source§

impl Eq for Terminal

Source§

impl Send for Terminal

Available on non-crate feature single-threaded only.
Source§

impl Sync for Terminal

Available on non-crate feature single-threaded only.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<W> WidgetId<W> for W
where W: WidgetExt + Clone + Send + Sync + 'static,

Source§

fn set_id(&mut self, id: &str)

Set the widget’s Id
Source§

fn with_id(self, id: &str) -> W

Construct a widget with an Id