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 ansi(&self) -> bool

Returns whether the terminal is in ANSI mode.

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

pub fn clear_mouse_selection(&mut self)

Clear any current mouse selection.

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

pub fn clear_cur_line(&mut self)

Clear entire line cursor is currently on.

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

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

Clear entire line for specified row.

Examples found in repository?
examples/terminal.rs (line 925)
702fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
703    term.take_focus().unwrap();
704
705    // Test the attr_fg_color and attr_bg_color methods.
706    // Put a single character 'A' into the buffer and check it
707    term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
708    term.set_text_bg_color(Color::TransparentBg);
709    term.set_text_fg_color(Color::XtermWhite);
710    term.append("A");
711    let r = &term.u8c_disp_row(0);
712    let uc = r.col(0);
713    assert_eq!(uc.text_utf8(), b"A");
714    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
715    assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
716    assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
717    assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
718    assert_eq!(&uc.attrib(), &Attrib::Normal);
719
720    // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
721    term.clear();
722    term.set_text_fg_color_xterm(XtermColor::White);
723    term.set_text_bg_color_xterm(XtermColor::Black);
724    assert_eq!(term.text_attrib(), Attrib::Normal);
725
726    assert!(term.ansi());
727    term.append("B\x1b[32mC\x1b[1mD\n");
728
729    let r = &term.u8c_disp_row(0);
730    let uc = r.col(0);
731    assert_eq!(uc.text_utf8(), b"B");
732    assert!(uc.is_char(b'B'));
733    assert!(!uc.is_char(b'A'));
734    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
735    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
736    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
737    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
738    assert_eq!(
739        &uc.charflags(),
740        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
741    );
742
743    let uc = r.col(1);
744    assert_eq!(uc.text_utf8(), b"C");
745    assert!(uc.is_char(b'C'));
746    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
747    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
748    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
749    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
750    assert_eq!(
751        &uc.charflags(),
752        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
753    );
754
755    let uc = r.col(2);
756    assert_eq!(uc.text_utf8(), b"D");
757    assert!(uc.is_char(b'D'));
758    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
759    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
760    assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
761    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
762    assert_eq!(
763        &uc.attr_fgcolor(Some(term)),
764        &Color::from_rgb(0x20, 0xf0, 0x20)
765    );
766    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
767    assert_eq!(
768        &uc.charflags(),
769        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
770    );
771
772    // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
773    term.clear();
774    term.set_text_fg_color_xterm(XtermColor::White);
775    term.set_text_bg_color_xterm(XtermColor::Black);
776    term.set_text_attrib(Attrib::Normal);
777    assert_eq!(term.text_attrib(), Attrib::Normal);
778
779    assert!(term.ansi());
780    term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
781
782    let r = &term.u8c_disp_row(0);
783    let uc = r.col(0);
784    assert_eq!(uc.text_utf8(), b"B");
785    assert!(uc.is_char(b'B'));
786    assert!(!uc.is_char(b'A'));
787    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
788    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
789    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
790    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
791    assert_eq!(
792        &uc.charflags(),
793        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
794    );
795
796    let uc = r.col(1);
797    assert_eq!(uc.text_utf8(), b"C");
798    assert!(uc.is_char(b'C'));
799    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
800    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
801    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
802    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
803    assert_eq!(
804        &uc.charflags(),
805        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
806    );
807
808    let uc = r.col(2);
809    assert_eq!(uc.text_utf8(), b"D");
810    assert!(uc.is_char(b'D'));
811    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
812    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
813    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
814    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
815    assert_eq!(
816        &uc.charflags(),
817        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
818    );
819
820    let uc = r.col(3);
821    assert_eq!(uc.text_utf8(), b"E");
822    assert!(uc.is_char(b'E'));
823    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
824    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
825    assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
826    assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
827    assert_eq!(
828        &uc.charflags(),
829        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
830    );
831
832    // Test some miscellaneous Utf8 constants
833    assert_eq!(uc.length(), 1);
834    assert_eq!(uc.max_utf8(), 4);
835    assert_eq!(uc.pwidth(), 9.0);
836    assert_eq!(uc.pwidth_int(), 9);
837
838    term.set_text_fg_color_xterm(XtermColor::White);
839    term.set_text_bg_color_xterm(XtermColor::Black);
840    term.clear();
841    term.set_text_attrib(Attrib::Normal);
842
843    // Mouse selection functions
844    term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
845    term.clear_mouse_selection();
846    assert_eq!(term.get_selection(), None);
847
848    // Play with cursor position
849    term.append("0123456789\n"); // Set up test pattern
850    term.append("ABCDEFGHIJ\n");
851    term.append("abcdefghij\n");
852
853    term.set_cursor_row(1);
854    assert_eq!(term.cursor_row(), 1);
855    term.set_cursor_col(1);
856    assert_eq!(term.cursor_col(), 1);
857    assert_eq!(term.u8c_cursor().text_utf8(), b"1");
858
859    term.append("----"); // Overwrites text at cursor and moves cursor forward
860    assert_eq!(term.cursor_row(), 1);
861    assert_eq!(term.cursor_col(), 5);
862    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
863    term.set_cursor_col(1);
864    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
865
866    term.cursor_up(1, false);
867    assert_eq!(term.cursor_row(), 0);
868    assert_eq!(term.cursor_col(), 1);
869    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
870
871    // Hit top of screen, so nothing happens
872    term.cursor_up(1, false);
873    assert_eq!(term.cursor_row(), 0);
874    assert_eq!(term.cursor_col(), 1);
875    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
876
877    // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
878    term.cursor_up(1, true);
879    assert_eq!(term.cursor_row(), 0);
880    assert_eq!(term.cursor_col(), 1);
881    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
882
883    // Go back down to the overwritten text
884    term.cursor_down(2, false);
885    assert_eq!(term.cursor_row(), 2);
886    assert_eq!(term.cursor_col(), 1);
887    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
888
889    // Go right past the overwritten text
890    term.cursor_right(4, false);
891    assert_eq!(term.cursor_row(), 2);
892    assert_eq!(term.cursor_col(), 5);
893    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
894
895    // Go left to the end of the overwritten text
896    term.cursor_left(1);
897    assert_eq!(term.cursor_row(), 2);
898    assert_eq!(term.cursor_col(), 4);
899    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
900
901    // Scroll back down, removing the blank line at the top.
902    // Cursor stays in place, the text moves under it.
903    term.scroll(1);
904    assert_eq!(term.cursor_row(), 2);
905    assert_eq!(term.cursor_col(), 4);
906    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
907
908    // Clear from here to end-of-line
909    term.clear_eol();
910    assert_eq!(term.cursor_row(), 2);
911    assert_eq!(term.cursor_col(), 4);
912    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
913
914    // Now clear from here to start-of-line. Cursor does not move.
915    term.clear_sol();
916    assert_eq!(term.cursor_row(), 2);
917    assert_eq!(term.cursor_col(), 4);
918    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
919    term.cursor_left(1);
920    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
921    term.set_cursor_col(0);
922    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
923
924    // Clear some lines
925    term.clear_line(1);
926    assert_eq!(term.cursor_row(), 2);
927    assert_eq!(term.cursor_col(), 0);
928    term.set_cursor_row(1);
929    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930    term.set_cursor_row(3);
931    term.clear_cur_line();
932    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
933    assert_eq!(term.cursor_row(), 3);
934    assert_eq!(term.cursor_col(), 0);
935
936    term.append("Two lines above are intentionally left blank.\n");
937    assert_eq!(term.cursor_row(), 4);
938    assert_eq!(term.cursor_col(), 0);
939
940    // Set up the test pattern again, then play with insert/delete
941    term.append("0123456789\n");
942    term.append("ABCDEFGHIJ\n");
943    term.append("abcdefghij\n");
944    assert_eq!(term.cursor_row(), 7);
945
946    term.set_cursor_row(4);
947    term.set_cursor_col(4);
948    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
949
950    term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
951    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
952    term.cursor_right(5, false);
953    assert_eq!(term.cursor_col(), 9);
954    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
955
956    // Insert two blank rows above cursor. Cursor stays put.
957    term.insert_rows(2);
958    assert_eq!(term.cursor_row(), 4);
959    assert_eq!(term.cursor_col(), 9);
960    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
961    term.cursor_down(2, false); // Go down to find our text again
962    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
963
964    // Go back to the beginning of the inserted 'x' characters and delete them.
965    term.cursor_left(5);
966    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
967    term.delete_cur_chars(5);
968    assert_eq!(term.cursor_row(), 6);
969    assert_eq!(term.cursor_col(), 4);
970    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
971
972    term.delete_chars(7, 2, 2); // Delete "CD" from the next row
973    term.cursor_down(1, false);
974    term.cursor_left(2);
975    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
976
977    term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
978    assert_eq!(term.u8c_cursor().text_utf8(), b"c");
979    term.cursor_up(1, false);
980    term.delete_rows(2); // Delete remains of test pattern
981
982    term.set_text_attrib(Attrib::Bold);
983    term.insert_char_eol('-', 3, 15, 20);
984    term.set_cursor_row(3);
985    term.set_cursor_col(15);
986    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
987    assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
988
989    term.set_text_attrib(Attrib::Italic);
990    term.append(" and all lines below");
991    term.set_text_attrib(Attrib::Normal);
992    term.cursor_down(1, false);
993
994    let mut hsb = term.hscrollbar();
995    let mut sb = term.scrollbar();
996    hsb.set_value(100.0);
997    assert_eq!(hsb.value(), 100.0);
998    sb.set_value(50.0);
999    assert_eq!(sb.value(), 50.0);
1000    hsb.set_value(0.0);
1001    assert_eq!(hsb.value(), 0.0);
1002    sb.set_value(0.0);
1003    assert_eq!(sb.value(), 0.0);
1004}
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 915)
702fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
703    term.take_focus().unwrap();
704
705    // Test the attr_fg_color and attr_bg_color methods.
706    // Put a single character 'A' into the buffer and check it
707    term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
708    term.set_text_bg_color(Color::TransparentBg);
709    term.set_text_fg_color(Color::XtermWhite);
710    term.append("A");
711    let r = &term.u8c_disp_row(0);
712    let uc = r.col(0);
713    assert_eq!(uc.text_utf8(), b"A");
714    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
715    assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
716    assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
717    assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
718    assert_eq!(&uc.attrib(), &Attrib::Normal);
719
720    // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
721    term.clear();
722    term.set_text_fg_color_xterm(XtermColor::White);
723    term.set_text_bg_color_xterm(XtermColor::Black);
724    assert_eq!(term.text_attrib(), Attrib::Normal);
725
726    assert!(term.ansi());
727    term.append("B\x1b[32mC\x1b[1mD\n");
728
729    let r = &term.u8c_disp_row(0);
730    let uc = r.col(0);
731    assert_eq!(uc.text_utf8(), b"B");
732    assert!(uc.is_char(b'B'));
733    assert!(!uc.is_char(b'A'));
734    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
735    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
736    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
737    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
738    assert_eq!(
739        &uc.charflags(),
740        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
741    );
742
743    let uc = r.col(1);
744    assert_eq!(uc.text_utf8(), b"C");
745    assert!(uc.is_char(b'C'));
746    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
747    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
748    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
749    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
750    assert_eq!(
751        &uc.charflags(),
752        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
753    );
754
755    let uc = r.col(2);
756    assert_eq!(uc.text_utf8(), b"D");
757    assert!(uc.is_char(b'D'));
758    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
759    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
760    assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
761    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
762    assert_eq!(
763        &uc.attr_fgcolor(Some(term)),
764        &Color::from_rgb(0x20, 0xf0, 0x20)
765    );
766    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
767    assert_eq!(
768        &uc.charflags(),
769        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
770    );
771
772    // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
773    term.clear();
774    term.set_text_fg_color_xterm(XtermColor::White);
775    term.set_text_bg_color_xterm(XtermColor::Black);
776    term.set_text_attrib(Attrib::Normal);
777    assert_eq!(term.text_attrib(), Attrib::Normal);
778
779    assert!(term.ansi());
780    term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
781
782    let r = &term.u8c_disp_row(0);
783    let uc = r.col(0);
784    assert_eq!(uc.text_utf8(), b"B");
785    assert!(uc.is_char(b'B'));
786    assert!(!uc.is_char(b'A'));
787    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
788    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
789    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
790    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
791    assert_eq!(
792        &uc.charflags(),
793        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
794    );
795
796    let uc = r.col(1);
797    assert_eq!(uc.text_utf8(), b"C");
798    assert!(uc.is_char(b'C'));
799    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
800    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
801    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
802    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
803    assert_eq!(
804        &uc.charflags(),
805        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
806    );
807
808    let uc = r.col(2);
809    assert_eq!(uc.text_utf8(), b"D");
810    assert!(uc.is_char(b'D'));
811    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
812    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
813    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
814    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
815    assert_eq!(
816        &uc.charflags(),
817        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
818    );
819
820    let uc = r.col(3);
821    assert_eq!(uc.text_utf8(), b"E");
822    assert!(uc.is_char(b'E'));
823    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
824    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
825    assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
826    assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
827    assert_eq!(
828        &uc.charflags(),
829        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
830    );
831
832    // Test some miscellaneous Utf8 constants
833    assert_eq!(uc.length(), 1);
834    assert_eq!(uc.max_utf8(), 4);
835    assert_eq!(uc.pwidth(), 9.0);
836    assert_eq!(uc.pwidth_int(), 9);
837
838    term.set_text_fg_color_xterm(XtermColor::White);
839    term.set_text_bg_color_xterm(XtermColor::Black);
840    term.clear();
841    term.set_text_attrib(Attrib::Normal);
842
843    // Mouse selection functions
844    term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
845    term.clear_mouse_selection();
846    assert_eq!(term.get_selection(), None);
847
848    // Play with cursor position
849    term.append("0123456789\n"); // Set up test pattern
850    term.append("ABCDEFGHIJ\n");
851    term.append("abcdefghij\n");
852
853    term.set_cursor_row(1);
854    assert_eq!(term.cursor_row(), 1);
855    term.set_cursor_col(1);
856    assert_eq!(term.cursor_col(), 1);
857    assert_eq!(term.u8c_cursor().text_utf8(), b"1");
858
859    term.append("----"); // Overwrites text at cursor and moves cursor forward
860    assert_eq!(term.cursor_row(), 1);
861    assert_eq!(term.cursor_col(), 5);
862    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
863    term.set_cursor_col(1);
864    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
865
866    term.cursor_up(1, false);
867    assert_eq!(term.cursor_row(), 0);
868    assert_eq!(term.cursor_col(), 1);
869    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
870
871    // Hit top of screen, so nothing happens
872    term.cursor_up(1, false);
873    assert_eq!(term.cursor_row(), 0);
874    assert_eq!(term.cursor_col(), 1);
875    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
876
877    // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
878    term.cursor_up(1, true);
879    assert_eq!(term.cursor_row(), 0);
880    assert_eq!(term.cursor_col(), 1);
881    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
882
883    // Go back down to the overwritten text
884    term.cursor_down(2, false);
885    assert_eq!(term.cursor_row(), 2);
886    assert_eq!(term.cursor_col(), 1);
887    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
888
889    // Go right past the overwritten text
890    term.cursor_right(4, false);
891    assert_eq!(term.cursor_row(), 2);
892    assert_eq!(term.cursor_col(), 5);
893    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
894
895    // Go left to the end of the overwritten text
896    term.cursor_left(1);
897    assert_eq!(term.cursor_row(), 2);
898    assert_eq!(term.cursor_col(), 4);
899    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
900
901    // Scroll back down, removing the blank line at the top.
902    // Cursor stays in place, the text moves under it.
903    term.scroll(1);
904    assert_eq!(term.cursor_row(), 2);
905    assert_eq!(term.cursor_col(), 4);
906    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
907
908    // Clear from here to end-of-line
909    term.clear_eol();
910    assert_eq!(term.cursor_row(), 2);
911    assert_eq!(term.cursor_col(), 4);
912    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
913
914    // Now clear from here to start-of-line. Cursor does not move.
915    term.clear_sol();
916    assert_eq!(term.cursor_row(), 2);
917    assert_eq!(term.cursor_col(), 4);
918    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
919    term.cursor_left(1);
920    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
921    term.set_cursor_col(0);
922    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
923
924    // Clear some lines
925    term.clear_line(1);
926    assert_eq!(term.cursor_row(), 2);
927    assert_eq!(term.cursor_col(), 0);
928    term.set_cursor_row(1);
929    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930    term.set_cursor_row(3);
931    term.clear_cur_line();
932    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
933    assert_eq!(term.cursor_row(), 3);
934    assert_eq!(term.cursor_col(), 0);
935
936    term.append("Two lines above are intentionally left blank.\n");
937    assert_eq!(term.cursor_row(), 4);
938    assert_eq!(term.cursor_col(), 0);
939
940    // Set up the test pattern again, then play with insert/delete
941    term.append("0123456789\n");
942    term.append("ABCDEFGHIJ\n");
943    term.append("abcdefghij\n");
944    assert_eq!(term.cursor_row(), 7);
945
946    term.set_cursor_row(4);
947    term.set_cursor_col(4);
948    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
949
950    term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
951    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
952    term.cursor_right(5, false);
953    assert_eq!(term.cursor_col(), 9);
954    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
955
956    // Insert two blank rows above cursor. Cursor stays put.
957    term.insert_rows(2);
958    assert_eq!(term.cursor_row(), 4);
959    assert_eq!(term.cursor_col(), 9);
960    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
961    term.cursor_down(2, false); // Go down to find our text again
962    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
963
964    // Go back to the beginning of the inserted 'x' characters and delete them.
965    term.cursor_left(5);
966    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
967    term.delete_cur_chars(5);
968    assert_eq!(term.cursor_row(), 6);
969    assert_eq!(term.cursor_col(), 4);
970    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
971
972    term.delete_chars(7, 2, 2); // Delete "CD" from the next row
973    term.cursor_down(1, false);
974    term.cursor_left(2);
975    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
976
977    term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
978    assert_eq!(term.u8c_cursor().text_utf8(), b"c");
979    term.cursor_up(1, false);
980    term.delete_rows(2); // Delete remains of test pattern
981
982    term.set_text_attrib(Attrib::Bold);
983    term.insert_char_eol('-', 3, 15, 20);
984    term.set_cursor_row(3);
985    term.set_cursor_col(15);
986    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
987    assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
988
989    term.set_text_attrib(Attrib::Italic);
990    term.append(" and all lines below");
991    term.set_text_attrib(Attrib::Normal);
992    term.cursor_down(1, false);
993
994    let mut hsb = term.hscrollbar();
995    let mut sb = term.scrollbar();
996    hsb.set_value(100.0);
997    assert_eq!(hsb.value(), 100.0);
998    sb.set_value(50.0);
999    assert_eq!(sb.value(), 50.0);
1000    hsb.set_value(0.0);
1001    assert_eq!(hsb.value(), 0.0);
1002    sb.set_value(0.0);
1003    assert_eq!(sb.value(), 0.0);
1004}
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 950)
702fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
703    term.take_focus().unwrap();
704
705    // Test the attr_fg_color and attr_bg_color methods.
706    // Put a single character 'A' into the buffer and check it
707    term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
708    term.set_text_bg_color(Color::TransparentBg);
709    term.set_text_fg_color(Color::XtermWhite);
710    term.append("A");
711    let r = &term.u8c_disp_row(0);
712    let uc = r.col(0);
713    assert_eq!(uc.text_utf8(), b"A");
714    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
715    assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
716    assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
717    assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
718    assert_eq!(&uc.attrib(), &Attrib::Normal);
719
720    // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
721    term.clear();
722    term.set_text_fg_color_xterm(XtermColor::White);
723    term.set_text_bg_color_xterm(XtermColor::Black);
724    assert_eq!(term.text_attrib(), Attrib::Normal);
725
726    assert!(term.ansi());
727    term.append("B\x1b[32mC\x1b[1mD\n");
728
729    let r = &term.u8c_disp_row(0);
730    let uc = r.col(0);
731    assert_eq!(uc.text_utf8(), b"B");
732    assert!(uc.is_char(b'B'));
733    assert!(!uc.is_char(b'A'));
734    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
735    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
736    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
737    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
738    assert_eq!(
739        &uc.charflags(),
740        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
741    );
742
743    let uc = r.col(1);
744    assert_eq!(uc.text_utf8(), b"C");
745    assert!(uc.is_char(b'C'));
746    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
747    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
748    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
749    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
750    assert_eq!(
751        &uc.charflags(),
752        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
753    );
754
755    let uc = r.col(2);
756    assert_eq!(uc.text_utf8(), b"D");
757    assert!(uc.is_char(b'D'));
758    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
759    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
760    assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
761    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
762    assert_eq!(
763        &uc.attr_fgcolor(Some(term)),
764        &Color::from_rgb(0x20, 0xf0, 0x20)
765    );
766    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
767    assert_eq!(
768        &uc.charflags(),
769        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
770    );
771
772    // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
773    term.clear();
774    term.set_text_fg_color_xterm(XtermColor::White);
775    term.set_text_bg_color_xterm(XtermColor::Black);
776    term.set_text_attrib(Attrib::Normal);
777    assert_eq!(term.text_attrib(), Attrib::Normal);
778
779    assert!(term.ansi());
780    term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
781
782    let r = &term.u8c_disp_row(0);
783    let uc = r.col(0);
784    assert_eq!(uc.text_utf8(), b"B");
785    assert!(uc.is_char(b'B'));
786    assert!(!uc.is_char(b'A'));
787    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
788    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
789    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
790    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
791    assert_eq!(
792        &uc.charflags(),
793        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
794    );
795
796    let uc = r.col(1);
797    assert_eq!(uc.text_utf8(), b"C");
798    assert!(uc.is_char(b'C'));
799    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
800    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
801    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
802    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
803    assert_eq!(
804        &uc.charflags(),
805        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
806    );
807
808    let uc = r.col(2);
809    assert_eq!(uc.text_utf8(), b"D");
810    assert!(uc.is_char(b'D'));
811    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
812    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
813    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
814    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
815    assert_eq!(
816        &uc.charflags(),
817        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
818    );
819
820    let uc = r.col(3);
821    assert_eq!(uc.text_utf8(), b"E");
822    assert!(uc.is_char(b'E'));
823    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
824    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
825    assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
826    assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
827    assert_eq!(
828        &uc.charflags(),
829        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
830    );
831
832    // Test some miscellaneous Utf8 constants
833    assert_eq!(uc.length(), 1);
834    assert_eq!(uc.max_utf8(), 4);
835    assert_eq!(uc.pwidth(), 9.0);
836    assert_eq!(uc.pwidth_int(), 9);
837
838    term.set_text_fg_color_xterm(XtermColor::White);
839    term.set_text_bg_color_xterm(XtermColor::Black);
840    term.clear();
841    term.set_text_attrib(Attrib::Normal);
842
843    // Mouse selection functions
844    term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
845    term.clear_mouse_selection();
846    assert_eq!(term.get_selection(), None);
847
848    // Play with cursor position
849    term.append("0123456789\n"); // Set up test pattern
850    term.append("ABCDEFGHIJ\n");
851    term.append("abcdefghij\n");
852
853    term.set_cursor_row(1);
854    assert_eq!(term.cursor_row(), 1);
855    term.set_cursor_col(1);
856    assert_eq!(term.cursor_col(), 1);
857    assert_eq!(term.u8c_cursor().text_utf8(), b"1");
858
859    term.append("----"); // Overwrites text at cursor and moves cursor forward
860    assert_eq!(term.cursor_row(), 1);
861    assert_eq!(term.cursor_col(), 5);
862    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
863    term.set_cursor_col(1);
864    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
865
866    term.cursor_up(1, false);
867    assert_eq!(term.cursor_row(), 0);
868    assert_eq!(term.cursor_col(), 1);
869    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
870
871    // Hit top of screen, so nothing happens
872    term.cursor_up(1, false);
873    assert_eq!(term.cursor_row(), 0);
874    assert_eq!(term.cursor_col(), 1);
875    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
876
877    // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
878    term.cursor_up(1, true);
879    assert_eq!(term.cursor_row(), 0);
880    assert_eq!(term.cursor_col(), 1);
881    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
882
883    // Go back down to the overwritten text
884    term.cursor_down(2, false);
885    assert_eq!(term.cursor_row(), 2);
886    assert_eq!(term.cursor_col(), 1);
887    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
888
889    // Go right past the overwritten text
890    term.cursor_right(4, false);
891    assert_eq!(term.cursor_row(), 2);
892    assert_eq!(term.cursor_col(), 5);
893    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
894
895    // Go left to the end of the overwritten text
896    term.cursor_left(1);
897    assert_eq!(term.cursor_row(), 2);
898    assert_eq!(term.cursor_col(), 4);
899    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
900
901    // Scroll back down, removing the blank line at the top.
902    // Cursor stays in place, the text moves under it.
903    term.scroll(1);
904    assert_eq!(term.cursor_row(), 2);
905    assert_eq!(term.cursor_col(), 4);
906    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
907
908    // Clear from here to end-of-line
909    term.clear_eol();
910    assert_eq!(term.cursor_row(), 2);
911    assert_eq!(term.cursor_col(), 4);
912    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
913
914    // Now clear from here to start-of-line. Cursor does not move.
915    term.clear_sol();
916    assert_eq!(term.cursor_row(), 2);
917    assert_eq!(term.cursor_col(), 4);
918    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
919    term.cursor_left(1);
920    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
921    term.set_cursor_col(0);
922    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
923
924    // Clear some lines
925    term.clear_line(1);
926    assert_eq!(term.cursor_row(), 2);
927    assert_eq!(term.cursor_col(), 0);
928    term.set_cursor_row(1);
929    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930    term.set_cursor_row(3);
931    term.clear_cur_line();
932    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
933    assert_eq!(term.cursor_row(), 3);
934    assert_eq!(term.cursor_col(), 0);
935
936    term.append("Two lines above are intentionally left blank.\n");
937    assert_eq!(term.cursor_row(), 4);
938    assert_eq!(term.cursor_col(), 0);
939
940    // Set up the test pattern again, then play with insert/delete
941    term.append("0123456789\n");
942    term.append("ABCDEFGHIJ\n");
943    term.append("abcdefghij\n");
944    assert_eq!(term.cursor_row(), 7);
945
946    term.set_cursor_row(4);
947    term.set_cursor_col(4);
948    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
949
950    term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
951    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
952    term.cursor_right(5, false);
953    assert_eq!(term.cursor_col(), 9);
954    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
955
956    // Insert two blank rows above cursor. Cursor stays put.
957    term.insert_rows(2);
958    assert_eq!(term.cursor_row(), 4);
959    assert_eq!(term.cursor_col(), 9);
960    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
961    term.cursor_down(2, false); // Go down to find our text again
962    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
963
964    // Go back to the beginning of the inserted 'x' characters and delete them.
965    term.cursor_left(5);
966    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
967    term.delete_cur_chars(5);
968    assert_eq!(term.cursor_row(), 6);
969    assert_eq!(term.cursor_col(), 4);
970    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
971
972    term.delete_chars(7, 2, 2); // Delete "CD" from the next row
973    term.cursor_down(1, false);
974    term.cursor_left(2);
975    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
976
977    term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
978    assert_eq!(term.u8c_cursor().text_utf8(), b"c");
979    term.cursor_up(1, false);
980    term.delete_rows(2); // Delete remains of test pattern
981
982    term.set_text_attrib(Attrib::Bold);
983    term.insert_char_eol('-', 3, 15, 20);
984    term.set_cursor_row(3);
985    term.set_cursor_col(15);
986    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
987    assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
988
989    term.set_text_attrib(Attrib::Italic);
990    term.append(" and all lines below");
991    term.set_text_attrib(Attrib::Normal);
992    term.cursor_down(1, false);
993
994    let mut hsb = term.hscrollbar();
995    let mut sb = term.scrollbar();
996    hsb.set_value(100.0);
997    assert_eq!(hsb.value(), 100.0);
998    sb.set_value(50.0);
999    assert_eq!(sb.value(), 50.0);
1000    hsb.set_value(0.0);
1001    assert_eq!(hsb.value(), 0.0);
1002    sb.set_value(0.0);
1003    assert_eq!(sb.value(), 0.0);
1004}
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 983)
702fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
703    term.take_focus().unwrap();
704
705    // Test the attr_fg_color and attr_bg_color methods.
706    // Put a single character 'A' into the buffer and check it
707    term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
708    term.set_text_bg_color(Color::TransparentBg);
709    term.set_text_fg_color(Color::XtermWhite);
710    term.append("A");
711    let r = &term.u8c_disp_row(0);
712    let uc = r.col(0);
713    assert_eq!(uc.text_utf8(), b"A");
714    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
715    assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
716    assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
717    assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
718    assert_eq!(&uc.attrib(), &Attrib::Normal);
719
720    // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
721    term.clear();
722    term.set_text_fg_color_xterm(XtermColor::White);
723    term.set_text_bg_color_xterm(XtermColor::Black);
724    assert_eq!(term.text_attrib(), Attrib::Normal);
725
726    assert!(term.ansi());
727    term.append("B\x1b[32mC\x1b[1mD\n");
728
729    let r = &term.u8c_disp_row(0);
730    let uc = r.col(0);
731    assert_eq!(uc.text_utf8(), b"B");
732    assert!(uc.is_char(b'B'));
733    assert!(!uc.is_char(b'A'));
734    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
735    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
736    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
737    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
738    assert_eq!(
739        &uc.charflags(),
740        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
741    );
742
743    let uc = r.col(1);
744    assert_eq!(uc.text_utf8(), b"C");
745    assert!(uc.is_char(b'C'));
746    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
747    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
748    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
749    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
750    assert_eq!(
751        &uc.charflags(),
752        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
753    );
754
755    let uc = r.col(2);
756    assert_eq!(uc.text_utf8(), b"D");
757    assert!(uc.is_char(b'D'));
758    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
759    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
760    assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
761    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
762    assert_eq!(
763        &uc.attr_fgcolor(Some(term)),
764        &Color::from_rgb(0x20, 0xf0, 0x20)
765    );
766    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
767    assert_eq!(
768        &uc.charflags(),
769        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
770    );
771
772    // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
773    term.clear();
774    term.set_text_fg_color_xterm(XtermColor::White);
775    term.set_text_bg_color_xterm(XtermColor::Black);
776    term.set_text_attrib(Attrib::Normal);
777    assert_eq!(term.text_attrib(), Attrib::Normal);
778
779    assert!(term.ansi());
780    term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
781
782    let r = &term.u8c_disp_row(0);
783    let uc = r.col(0);
784    assert_eq!(uc.text_utf8(), b"B");
785    assert!(uc.is_char(b'B'));
786    assert!(!uc.is_char(b'A'));
787    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
788    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
789    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
790    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
791    assert_eq!(
792        &uc.charflags(),
793        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
794    );
795
796    let uc = r.col(1);
797    assert_eq!(uc.text_utf8(), b"C");
798    assert!(uc.is_char(b'C'));
799    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
800    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
801    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
802    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
803    assert_eq!(
804        &uc.charflags(),
805        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
806    );
807
808    let uc = r.col(2);
809    assert_eq!(uc.text_utf8(), b"D");
810    assert!(uc.is_char(b'D'));
811    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
812    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
813    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
814    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
815    assert_eq!(
816        &uc.charflags(),
817        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
818    );
819
820    let uc = r.col(3);
821    assert_eq!(uc.text_utf8(), b"E");
822    assert!(uc.is_char(b'E'));
823    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
824    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
825    assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
826    assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
827    assert_eq!(
828        &uc.charflags(),
829        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
830    );
831
832    // Test some miscellaneous Utf8 constants
833    assert_eq!(uc.length(), 1);
834    assert_eq!(uc.max_utf8(), 4);
835    assert_eq!(uc.pwidth(), 9.0);
836    assert_eq!(uc.pwidth_int(), 9);
837
838    term.set_text_fg_color_xterm(XtermColor::White);
839    term.set_text_bg_color_xterm(XtermColor::Black);
840    term.clear();
841    term.set_text_attrib(Attrib::Normal);
842
843    // Mouse selection functions
844    term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
845    term.clear_mouse_selection();
846    assert_eq!(term.get_selection(), None);
847
848    // Play with cursor position
849    term.append("0123456789\n"); // Set up test pattern
850    term.append("ABCDEFGHIJ\n");
851    term.append("abcdefghij\n");
852
853    term.set_cursor_row(1);
854    assert_eq!(term.cursor_row(), 1);
855    term.set_cursor_col(1);
856    assert_eq!(term.cursor_col(), 1);
857    assert_eq!(term.u8c_cursor().text_utf8(), b"1");
858
859    term.append("----"); // Overwrites text at cursor and moves cursor forward
860    assert_eq!(term.cursor_row(), 1);
861    assert_eq!(term.cursor_col(), 5);
862    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
863    term.set_cursor_col(1);
864    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
865
866    term.cursor_up(1, false);
867    assert_eq!(term.cursor_row(), 0);
868    assert_eq!(term.cursor_col(), 1);
869    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
870
871    // Hit top of screen, so nothing happens
872    term.cursor_up(1, false);
873    assert_eq!(term.cursor_row(), 0);
874    assert_eq!(term.cursor_col(), 1);
875    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
876
877    // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
878    term.cursor_up(1, true);
879    assert_eq!(term.cursor_row(), 0);
880    assert_eq!(term.cursor_col(), 1);
881    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
882
883    // Go back down to the overwritten text
884    term.cursor_down(2, false);
885    assert_eq!(term.cursor_row(), 2);
886    assert_eq!(term.cursor_col(), 1);
887    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
888
889    // Go right past the overwritten text
890    term.cursor_right(4, false);
891    assert_eq!(term.cursor_row(), 2);
892    assert_eq!(term.cursor_col(), 5);
893    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
894
895    // Go left to the end of the overwritten text
896    term.cursor_left(1);
897    assert_eq!(term.cursor_row(), 2);
898    assert_eq!(term.cursor_col(), 4);
899    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
900
901    // Scroll back down, removing the blank line at the top.
902    // Cursor stays in place, the text moves under it.
903    term.scroll(1);
904    assert_eq!(term.cursor_row(), 2);
905    assert_eq!(term.cursor_col(), 4);
906    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
907
908    // Clear from here to end-of-line
909    term.clear_eol();
910    assert_eq!(term.cursor_row(), 2);
911    assert_eq!(term.cursor_col(), 4);
912    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
913
914    // Now clear from here to start-of-line. Cursor does not move.
915    term.clear_sol();
916    assert_eq!(term.cursor_row(), 2);
917    assert_eq!(term.cursor_col(), 4);
918    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
919    term.cursor_left(1);
920    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
921    term.set_cursor_col(0);
922    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
923
924    // Clear some lines
925    term.clear_line(1);
926    assert_eq!(term.cursor_row(), 2);
927    assert_eq!(term.cursor_col(), 0);
928    term.set_cursor_row(1);
929    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930    term.set_cursor_row(3);
931    term.clear_cur_line();
932    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
933    assert_eq!(term.cursor_row(), 3);
934    assert_eq!(term.cursor_col(), 0);
935
936    term.append("Two lines above are intentionally left blank.\n");
937    assert_eq!(term.cursor_row(), 4);
938    assert_eq!(term.cursor_col(), 0);
939
940    // Set up the test pattern again, then play with insert/delete
941    term.append("0123456789\n");
942    term.append("ABCDEFGHIJ\n");
943    term.append("abcdefghij\n");
944    assert_eq!(term.cursor_row(), 7);
945
946    term.set_cursor_row(4);
947    term.set_cursor_col(4);
948    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
949
950    term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
951    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
952    term.cursor_right(5, false);
953    assert_eq!(term.cursor_col(), 9);
954    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
955
956    // Insert two blank rows above cursor. Cursor stays put.
957    term.insert_rows(2);
958    assert_eq!(term.cursor_row(), 4);
959    assert_eq!(term.cursor_col(), 9);
960    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
961    term.cursor_down(2, false); // Go down to find our text again
962    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
963
964    // Go back to the beginning of the inserted 'x' characters and delete them.
965    term.cursor_left(5);
966    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
967    term.delete_cur_chars(5);
968    assert_eq!(term.cursor_row(), 6);
969    assert_eq!(term.cursor_col(), 4);
970    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
971
972    term.delete_chars(7, 2, 2); // Delete "CD" from the next row
973    term.cursor_down(1, false);
974    term.cursor_left(2);
975    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
976
977    term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
978    assert_eq!(term.u8c_cursor().text_utf8(), b"c");
979    term.cursor_up(1, false);
980    term.delete_rows(2); // Delete remains of test pattern
981
982    term.set_text_attrib(Attrib::Bold);
983    term.insert_char_eol('-', 3, 15, 20);
984    term.set_cursor_row(3);
985    term.set_cursor_col(15);
986    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
987    assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
988
989    term.set_text_attrib(Attrib::Italic);
990    term.append(" and all lines below");
991    term.set_text_attrib(Attrib::Normal);
992    term.cursor_down(1, false);
993
994    let mut hsb = term.hscrollbar();
995    let mut sb = term.scrollbar();
996    hsb.set_value(100.0);
997    assert_eq!(hsb.value(), 100.0);
998    sb.set_value(50.0);
999    assert_eq!(sb.value(), 50.0);
1000    hsb.set_value(0.0);
1001    assert_eq!(hsb.value(), 0.0);
1002    sb.set_value(0.0);
1003    assert_eq!(sb.value(), 0.0);
1004}
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 957)
702fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
703    term.take_focus().unwrap();
704
705    // Test the attr_fg_color and attr_bg_color methods.
706    // Put a single character 'A' into the buffer and check it
707    term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
708    term.set_text_bg_color(Color::TransparentBg);
709    term.set_text_fg_color(Color::XtermWhite);
710    term.append("A");
711    let r = &term.u8c_disp_row(0);
712    let uc = r.col(0);
713    assert_eq!(uc.text_utf8(), b"A");
714    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
715    assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
716    assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
717    assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
718    assert_eq!(&uc.attrib(), &Attrib::Normal);
719
720    // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
721    term.clear();
722    term.set_text_fg_color_xterm(XtermColor::White);
723    term.set_text_bg_color_xterm(XtermColor::Black);
724    assert_eq!(term.text_attrib(), Attrib::Normal);
725
726    assert!(term.ansi());
727    term.append("B\x1b[32mC\x1b[1mD\n");
728
729    let r = &term.u8c_disp_row(0);
730    let uc = r.col(0);
731    assert_eq!(uc.text_utf8(), b"B");
732    assert!(uc.is_char(b'B'));
733    assert!(!uc.is_char(b'A'));
734    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
735    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
736    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
737    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
738    assert_eq!(
739        &uc.charflags(),
740        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
741    );
742
743    let uc = r.col(1);
744    assert_eq!(uc.text_utf8(), b"C");
745    assert!(uc.is_char(b'C'));
746    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
747    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
748    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
749    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
750    assert_eq!(
751        &uc.charflags(),
752        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
753    );
754
755    let uc = r.col(2);
756    assert_eq!(uc.text_utf8(), b"D");
757    assert!(uc.is_char(b'D'));
758    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
759    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
760    assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
761    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
762    assert_eq!(
763        &uc.attr_fgcolor(Some(term)),
764        &Color::from_rgb(0x20, 0xf0, 0x20)
765    );
766    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
767    assert_eq!(
768        &uc.charflags(),
769        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
770    );
771
772    // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
773    term.clear();
774    term.set_text_fg_color_xterm(XtermColor::White);
775    term.set_text_bg_color_xterm(XtermColor::Black);
776    term.set_text_attrib(Attrib::Normal);
777    assert_eq!(term.text_attrib(), Attrib::Normal);
778
779    assert!(term.ansi());
780    term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
781
782    let r = &term.u8c_disp_row(0);
783    let uc = r.col(0);
784    assert_eq!(uc.text_utf8(), b"B");
785    assert!(uc.is_char(b'B'));
786    assert!(!uc.is_char(b'A'));
787    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
788    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
789    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
790    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
791    assert_eq!(
792        &uc.charflags(),
793        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
794    );
795
796    let uc = r.col(1);
797    assert_eq!(uc.text_utf8(), b"C");
798    assert!(uc.is_char(b'C'));
799    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
800    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
801    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
802    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
803    assert_eq!(
804        &uc.charflags(),
805        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
806    );
807
808    let uc = r.col(2);
809    assert_eq!(uc.text_utf8(), b"D");
810    assert!(uc.is_char(b'D'));
811    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
812    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
813    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
814    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
815    assert_eq!(
816        &uc.charflags(),
817        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
818    );
819
820    let uc = r.col(3);
821    assert_eq!(uc.text_utf8(), b"E");
822    assert!(uc.is_char(b'E'));
823    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
824    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
825    assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
826    assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
827    assert_eq!(
828        &uc.charflags(),
829        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
830    );
831
832    // Test some miscellaneous Utf8 constants
833    assert_eq!(uc.length(), 1);
834    assert_eq!(uc.max_utf8(), 4);
835    assert_eq!(uc.pwidth(), 9.0);
836    assert_eq!(uc.pwidth_int(), 9);
837
838    term.set_text_fg_color_xterm(XtermColor::White);
839    term.set_text_bg_color_xterm(XtermColor::Black);
840    term.clear();
841    term.set_text_attrib(Attrib::Normal);
842
843    // Mouse selection functions
844    term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
845    term.clear_mouse_selection();
846    assert_eq!(term.get_selection(), None);
847
848    // Play with cursor position
849    term.append("0123456789\n"); // Set up test pattern
850    term.append("ABCDEFGHIJ\n");
851    term.append("abcdefghij\n");
852
853    term.set_cursor_row(1);
854    assert_eq!(term.cursor_row(), 1);
855    term.set_cursor_col(1);
856    assert_eq!(term.cursor_col(), 1);
857    assert_eq!(term.u8c_cursor().text_utf8(), b"1");
858
859    term.append("----"); // Overwrites text at cursor and moves cursor forward
860    assert_eq!(term.cursor_row(), 1);
861    assert_eq!(term.cursor_col(), 5);
862    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
863    term.set_cursor_col(1);
864    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
865
866    term.cursor_up(1, false);
867    assert_eq!(term.cursor_row(), 0);
868    assert_eq!(term.cursor_col(), 1);
869    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
870
871    // Hit top of screen, so nothing happens
872    term.cursor_up(1, false);
873    assert_eq!(term.cursor_row(), 0);
874    assert_eq!(term.cursor_col(), 1);
875    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
876
877    // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
878    term.cursor_up(1, true);
879    assert_eq!(term.cursor_row(), 0);
880    assert_eq!(term.cursor_col(), 1);
881    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
882
883    // Go back down to the overwritten text
884    term.cursor_down(2, false);
885    assert_eq!(term.cursor_row(), 2);
886    assert_eq!(term.cursor_col(), 1);
887    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
888
889    // Go right past the overwritten text
890    term.cursor_right(4, false);
891    assert_eq!(term.cursor_row(), 2);
892    assert_eq!(term.cursor_col(), 5);
893    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
894
895    // Go left to the end of the overwritten text
896    term.cursor_left(1);
897    assert_eq!(term.cursor_row(), 2);
898    assert_eq!(term.cursor_col(), 4);
899    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
900
901    // Scroll back down, removing the blank line at the top.
902    // Cursor stays in place, the text moves under it.
903    term.scroll(1);
904    assert_eq!(term.cursor_row(), 2);
905    assert_eq!(term.cursor_col(), 4);
906    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
907
908    // Clear from here to end-of-line
909    term.clear_eol();
910    assert_eq!(term.cursor_row(), 2);
911    assert_eq!(term.cursor_col(), 4);
912    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
913
914    // Now clear from here to start-of-line. Cursor does not move.
915    term.clear_sol();
916    assert_eq!(term.cursor_row(), 2);
917    assert_eq!(term.cursor_col(), 4);
918    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
919    term.cursor_left(1);
920    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
921    term.set_cursor_col(0);
922    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
923
924    // Clear some lines
925    term.clear_line(1);
926    assert_eq!(term.cursor_row(), 2);
927    assert_eq!(term.cursor_col(), 0);
928    term.set_cursor_row(1);
929    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930    term.set_cursor_row(3);
931    term.clear_cur_line();
932    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
933    assert_eq!(term.cursor_row(), 3);
934    assert_eq!(term.cursor_col(), 0);
935
936    term.append("Two lines above are intentionally left blank.\n");
937    assert_eq!(term.cursor_row(), 4);
938    assert_eq!(term.cursor_col(), 0);
939
940    // Set up the test pattern again, then play with insert/delete
941    term.append("0123456789\n");
942    term.append("ABCDEFGHIJ\n");
943    term.append("abcdefghij\n");
944    assert_eq!(term.cursor_row(), 7);
945
946    term.set_cursor_row(4);
947    term.set_cursor_col(4);
948    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
949
950    term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
951    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
952    term.cursor_right(5, false);
953    assert_eq!(term.cursor_col(), 9);
954    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
955
956    // Insert two blank rows above cursor. Cursor stays put.
957    term.insert_rows(2);
958    assert_eq!(term.cursor_row(), 4);
959    assert_eq!(term.cursor_col(), 9);
960    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
961    term.cursor_down(2, false); // Go down to find our text again
962    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
963
964    // Go back to the beginning of the inserted 'x' characters and delete them.
965    term.cursor_left(5);
966    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
967    term.delete_cur_chars(5);
968    assert_eq!(term.cursor_row(), 6);
969    assert_eq!(term.cursor_col(), 4);
970    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
971
972    term.delete_chars(7, 2, 2); // Delete "CD" from the next row
973    term.cursor_down(1, false);
974    term.cursor_left(2);
975    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
976
977    term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
978    assert_eq!(term.u8c_cursor().text_utf8(), b"c");
979    term.cursor_up(1, false);
980    term.delete_rows(2); // Delete remains of test pattern
981
982    term.set_text_attrib(Attrib::Bold);
983    term.insert_char_eol('-', 3, 15, 20);
984    term.set_cursor_row(3);
985    term.set_cursor_col(15);
986    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
987    assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
988
989    term.set_text_attrib(Attrib::Italic);
990    term.append(" and all lines below");
991    term.set_text_attrib(Attrib::Normal);
992    term.cursor_down(1, false);
993
994    let mut hsb = term.hscrollbar();
995    let mut sb = term.scrollbar();
996    hsb.set_value(100.0);
997    assert_eq!(hsb.value(), 100.0);
998    sb.set_value(50.0);
999    assert_eq!(sb.value(), 50.0);
1000    hsb.set_value(0.0);
1001    assert_eq!(hsb.value(), 0.0);
1002    sb.set_value(0.0);
1003    assert_eq!(sb.value(), 0.0);
1004}
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 972)
702fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
703    term.take_focus().unwrap();
704
705    // Test the attr_fg_color and attr_bg_color methods.
706    // Put a single character 'A' into the buffer and check it
707    term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
708    term.set_text_bg_color(Color::TransparentBg);
709    term.set_text_fg_color(Color::XtermWhite);
710    term.append("A");
711    let r = &term.u8c_disp_row(0);
712    let uc = r.col(0);
713    assert_eq!(uc.text_utf8(), b"A");
714    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
715    assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
716    assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
717    assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
718    assert_eq!(&uc.attrib(), &Attrib::Normal);
719
720    // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
721    term.clear();
722    term.set_text_fg_color_xterm(XtermColor::White);
723    term.set_text_bg_color_xterm(XtermColor::Black);
724    assert_eq!(term.text_attrib(), Attrib::Normal);
725
726    assert!(term.ansi());
727    term.append("B\x1b[32mC\x1b[1mD\n");
728
729    let r = &term.u8c_disp_row(0);
730    let uc = r.col(0);
731    assert_eq!(uc.text_utf8(), b"B");
732    assert!(uc.is_char(b'B'));
733    assert!(!uc.is_char(b'A'));
734    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
735    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
736    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
737    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
738    assert_eq!(
739        &uc.charflags(),
740        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
741    );
742
743    let uc = r.col(1);
744    assert_eq!(uc.text_utf8(), b"C");
745    assert!(uc.is_char(b'C'));
746    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
747    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
748    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
749    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
750    assert_eq!(
751        &uc.charflags(),
752        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
753    );
754
755    let uc = r.col(2);
756    assert_eq!(uc.text_utf8(), b"D");
757    assert!(uc.is_char(b'D'));
758    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
759    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
760    assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
761    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
762    assert_eq!(
763        &uc.attr_fgcolor(Some(term)),
764        &Color::from_rgb(0x20, 0xf0, 0x20)
765    );
766    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
767    assert_eq!(
768        &uc.charflags(),
769        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
770    );
771
772    // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
773    term.clear();
774    term.set_text_fg_color_xterm(XtermColor::White);
775    term.set_text_bg_color_xterm(XtermColor::Black);
776    term.set_text_attrib(Attrib::Normal);
777    assert_eq!(term.text_attrib(), Attrib::Normal);
778
779    assert!(term.ansi());
780    term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
781
782    let r = &term.u8c_disp_row(0);
783    let uc = r.col(0);
784    assert_eq!(uc.text_utf8(), b"B");
785    assert!(uc.is_char(b'B'));
786    assert!(!uc.is_char(b'A'));
787    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
788    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
789    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
790    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
791    assert_eq!(
792        &uc.charflags(),
793        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
794    );
795
796    let uc = r.col(1);
797    assert_eq!(uc.text_utf8(), b"C");
798    assert!(uc.is_char(b'C'));
799    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
800    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
801    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
802    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
803    assert_eq!(
804        &uc.charflags(),
805        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
806    );
807
808    let uc = r.col(2);
809    assert_eq!(uc.text_utf8(), b"D");
810    assert!(uc.is_char(b'D'));
811    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
812    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
813    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
814    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
815    assert_eq!(
816        &uc.charflags(),
817        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
818    );
819
820    let uc = r.col(3);
821    assert_eq!(uc.text_utf8(), b"E");
822    assert!(uc.is_char(b'E'));
823    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
824    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
825    assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
826    assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
827    assert_eq!(
828        &uc.charflags(),
829        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
830    );
831
832    // Test some miscellaneous Utf8 constants
833    assert_eq!(uc.length(), 1);
834    assert_eq!(uc.max_utf8(), 4);
835    assert_eq!(uc.pwidth(), 9.0);
836    assert_eq!(uc.pwidth_int(), 9);
837
838    term.set_text_fg_color_xterm(XtermColor::White);
839    term.set_text_bg_color_xterm(XtermColor::Black);
840    term.clear();
841    term.set_text_attrib(Attrib::Normal);
842
843    // Mouse selection functions
844    term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
845    term.clear_mouse_selection();
846    assert_eq!(term.get_selection(), None);
847
848    // Play with cursor position
849    term.append("0123456789\n"); // Set up test pattern
850    term.append("ABCDEFGHIJ\n");
851    term.append("abcdefghij\n");
852
853    term.set_cursor_row(1);
854    assert_eq!(term.cursor_row(), 1);
855    term.set_cursor_col(1);
856    assert_eq!(term.cursor_col(), 1);
857    assert_eq!(term.u8c_cursor().text_utf8(), b"1");
858
859    term.append("----"); // Overwrites text at cursor and moves cursor forward
860    assert_eq!(term.cursor_row(), 1);
861    assert_eq!(term.cursor_col(), 5);
862    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
863    term.set_cursor_col(1);
864    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
865
866    term.cursor_up(1, false);
867    assert_eq!(term.cursor_row(), 0);
868    assert_eq!(term.cursor_col(), 1);
869    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
870
871    // Hit top of screen, so nothing happens
872    term.cursor_up(1, false);
873    assert_eq!(term.cursor_row(), 0);
874    assert_eq!(term.cursor_col(), 1);
875    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
876
877    // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
878    term.cursor_up(1, true);
879    assert_eq!(term.cursor_row(), 0);
880    assert_eq!(term.cursor_col(), 1);
881    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
882
883    // Go back down to the overwritten text
884    term.cursor_down(2, false);
885    assert_eq!(term.cursor_row(), 2);
886    assert_eq!(term.cursor_col(), 1);
887    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
888
889    // Go right past the overwritten text
890    term.cursor_right(4, false);
891    assert_eq!(term.cursor_row(), 2);
892    assert_eq!(term.cursor_col(), 5);
893    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
894
895    // Go left to the end of the overwritten text
896    term.cursor_left(1);
897    assert_eq!(term.cursor_row(), 2);
898    assert_eq!(term.cursor_col(), 4);
899    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
900
901    // Scroll back down, removing the blank line at the top.
902    // Cursor stays in place, the text moves under it.
903    term.scroll(1);
904    assert_eq!(term.cursor_row(), 2);
905    assert_eq!(term.cursor_col(), 4);
906    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
907
908    // Clear from here to end-of-line
909    term.clear_eol();
910    assert_eq!(term.cursor_row(), 2);
911    assert_eq!(term.cursor_col(), 4);
912    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
913
914    // Now clear from here to start-of-line. Cursor does not move.
915    term.clear_sol();
916    assert_eq!(term.cursor_row(), 2);
917    assert_eq!(term.cursor_col(), 4);
918    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
919    term.cursor_left(1);
920    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
921    term.set_cursor_col(0);
922    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
923
924    // Clear some lines
925    term.clear_line(1);
926    assert_eq!(term.cursor_row(), 2);
927    assert_eq!(term.cursor_col(), 0);
928    term.set_cursor_row(1);
929    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930    term.set_cursor_row(3);
931    term.clear_cur_line();
932    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
933    assert_eq!(term.cursor_row(), 3);
934    assert_eq!(term.cursor_col(), 0);
935
936    term.append("Two lines above are intentionally left blank.\n");
937    assert_eq!(term.cursor_row(), 4);
938    assert_eq!(term.cursor_col(), 0);
939
940    // Set up the test pattern again, then play with insert/delete
941    term.append("0123456789\n");
942    term.append("ABCDEFGHIJ\n");
943    term.append("abcdefghij\n");
944    assert_eq!(term.cursor_row(), 7);
945
946    term.set_cursor_row(4);
947    term.set_cursor_col(4);
948    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
949
950    term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
951    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
952    term.cursor_right(5, false);
953    assert_eq!(term.cursor_col(), 9);
954    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
955
956    // Insert two blank rows above cursor. Cursor stays put.
957    term.insert_rows(2);
958    assert_eq!(term.cursor_row(), 4);
959    assert_eq!(term.cursor_col(), 9);
960    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
961    term.cursor_down(2, false); // Go down to find our text again
962    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
963
964    // Go back to the beginning of the inserted 'x' characters and delete them.
965    term.cursor_left(5);
966    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
967    term.delete_cur_chars(5);
968    assert_eq!(term.cursor_row(), 6);
969    assert_eq!(term.cursor_col(), 4);
970    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
971
972    term.delete_chars(7, 2, 2); // Delete "CD" from the next row
973    term.cursor_down(1, false);
974    term.cursor_left(2);
975    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
976
977    term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
978    assert_eq!(term.u8c_cursor().text_utf8(), b"c");
979    term.cursor_up(1, false);
980    term.delete_rows(2); // Delete remains of test pattern
981
982    term.set_text_attrib(Attrib::Bold);
983    term.insert_char_eol('-', 3, 15, 20);
984    term.set_cursor_row(3);
985    term.set_cursor_col(15);
986    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
987    assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
988
989    term.set_text_attrib(Attrib::Italic);
990    term.append(" and all lines below");
991    term.set_text_attrib(Attrib::Normal);
992    term.cursor_down(1, false);
993
994    let mut hsb = term.hscrollbar();
995    let mut sb = term.scrollbar();
996    hsb.set_value(100.0);
997    assert_eq!(hsb.value(), 100.0);
998    sb.set_value(50.0);
999    assert_eq!(sb.value(), 50.0);
1000    hsb.set_value(0.0);
1001    assert_eq!(hsb.value(), 0.0);
1002    sb.set_value(0.0);
1003    assert_eq!(sb.value(), 0.0);
1004}
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 967)
702fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
703    term.take_focus().unwrap();
704
705    // Test the attr_fg_color and attr_bg_color methods.
706    // Put a single character 'A' into the buffer and check it
707    term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
708    term.set_text_bg_color(Color::TransparentBg);
709    term.set_text_fg_color(Color::XtermWhite);
710    term.append("A");
711    let r = &term.u8c_disp_row(0);
712    let uc = r.col(0);
713    assert_eq!(uc.text_utf8(), b"A");
714    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
715    assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
716    assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
717    assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
718    assert_eq!(&uc.attrib(), &Attrib::Normal);
719
720    // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
721    term.clear();
722    term.set_text_fg_color_xterm(XtermColor::White);
723    term.set_text_bg_color_xterm(XtermColor::Black);
724    assert_eq!(term.text_attrib(), Attrib::Normal);
725
726    assert!(term.ansi());
727    term.append("B\x1b[32mC\x1b[1mD\n");
728
729    let r = &term.u8c_disp_row(0);
730    let uc = r.col(0);
731    assert_eq!(uc.text_utf8(), b"B");
732    assert!(uc.is_char(b'B'));
733    assert!(!uc.is_char(b'A'));
734    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
735    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
736    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
737    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
738    assert_eq!(
739        &uc.charflags(),
740        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
741    );
742
743    let uc = r.col(1);
744    assert_eq!(uc.text_utf8(), b"C");
745    assert!(uc.is_char(b'C'));
746    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
747    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
748    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
749    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
750    assert_eq!(
751        &uc.charflags(),
752        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
753    );
754
755    let uc = r.col(2);
756    assert_eq!(uc.text_utf8(), b"D");
757    assert!(uc.is_char(b'D'));
758    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
759    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
760    assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
761    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
762    assert_eq!(
763        &uc.attr_fgcolor(Some(term)),
764        &Color::from_rgb(0x20, 0xf0, 0x20)
765    );
766    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
767    assert_eq!(
768        &uc.charflags(),
769        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
770    );
771
772    // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
773    term.clear();
774    term.set_text_fg_color_xterm(XtermColor::White);
775    term.set_text_bg_color_xterm(XtermColor::Black);
776    term.set_text_attrib(Attrib::Normal);
777    assert_eq!(term.text_attrib(), Attrib::Normal);
778
779    assert!(term.ansi());
780    term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
781
782    let r = &term.u8c_disp_row(0);
783    let uc = r.col(0);
784    assert_eq!(uc.text_utf8(), b"B");
785    assert!(uc.is_char(b'B'));
786    assert!(!uc.is_char(b'A'));
787    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
788    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
789    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
790    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
791    assert_eq!(
792        &uc.charflags(),
793        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
794    );
795
796    let uc = r.col(1);
797    assert_eq!(uc.text_utf8(), b"C");
798    assert!(uc.is_char(b'C'));
799    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
800    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
801    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
802    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
803    assert_eq!(
804        &uc.charflags(),
805        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
806    );
807
808    let uc = r.col(2);
809    assert_eq!(uc.text_utf8(), b"D");
810    assert!(uc.is_char(b'D'));
811    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
812    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
813    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
814    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
815    assert_eq!(
816        &uc.charflags(),
817        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
818    );
819
820    let uc = r.col(3);
821    assert_eq!(uc.text_utf8(), b"E");
822    assert!(uc.is_char(b'E'));
823    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
824    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
825    assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
826    assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
827    assert_eq!(
828        &uc.charflags(),
829        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
830    );
831
832    // Test some miscellaneous Utf8 constants
833    assert_eq!(uc.length(), 1);
834    assert_eq!(uc.max_utf8(), 4);
835    assert_eq!(uc.pwidth(), 9.0);
836    assert_eq!(uc.pwidth_int(), 9);
837
838    term.set_text_fg_color_xterm(XtermColor::White);
839    term.set_text_bg_color_xterm(XtermColor::Black);
840    term.clear();
841    term.set_text_attrib(Attrib::Normal);
842
843    // Mouse selection functions
844    term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
845    term.clear_mouse_selection();
846    assert_eq!(term.get_selection(), None);
847
848    // Play with cursor position
849    term.append("0123456789\n"); // Set up test pattern
850    term.append("ABCDEFGHIJ\n");
851    term.append("abcdefghij\n");
852
853    term.set_cursor_row(1);
854    assert_eq!(term.cursor_row(), 1);
855    term.set_cursor_col(1);
856    assert_eq!(term.cursor_col(), 1);
857    assert_eq!(term.u8c_cursor().text_utf8(), b"1");
858
859    term.append("----"); // Overwrites text at cursor and moves cursor forward
860    assert_eq!(term.cursor_row(), 1);
861    assert_eq!(term.cursor_col(), 5);
862    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
863    term.set_cursor_col(1);
864    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
865
866    term.cursor_up(1, false);
867    assert_eq!(term.cursor_row(), 0);
868    assert_eq!(term.cursor_col(), 1);
869    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
870
871    // Hit top of screen, so nothing happens
872    term.cursor_up(1, false);
873    assert_eq!(term.cursor_row(), 0);
874    assert_eq!(term.cursor_col(), 1);
875    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
876
877    // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
878    term.cursor_up(1, true);
879    assert_eq!(term.cursor_row(), 0);
880    assert_eq!(term.cursor_col(), 1);
881    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
882
883    // Go back down to the overwritten text
884    term.cursor_down(2, false);
885    assert_eq!(term.cursor_row(), 2);
886    assert_eq!(term.cursor_col(), 1);
887    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
888
889    // Go right past the overwritten text
890    term.cursor_right(4, false);
891    assert_eq!(term.cursor_row(), 2);
892    assert_eq!(term.cursor_col(), 5);
893    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
894
895    // Go left to the end of the overwritten text
896    term.cursor_left(1);
897    assert_eq!(term.cursor_row(), 2);
898    assert_eq!(term.cursor_col(), 4);
899    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
900
901    // Scroll back down, removing the blank line at the top.
902    // Cursor stays in place, the text moves under it.
903    term.scroll(1);
904    assert_eq!(term.cursor_row(), 2);
905    assert_eq!(term.cursor_col(), 4);
906    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
907
908    // Clear from here to end-of-line
909    term.clear_eol();
910    assert_eq!(term.cursor_row(), 2);
911    assert_eq!(term.cursor_col(), 4);
912    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
913
914    // Now clear from here to start-of-line. Cursor does not move.
915    term.clear_sol();
916    assert_eq!(term.cursor_row(), 2);
917    assert_eq!(term.cursor_col(), 4);
918    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
919    term.cursor_left(1);
920    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
921    term.set_cursor_col(0);
922    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
923
924    // Clear some lines
925    term.clear_line(1);
926    assert_eq!(term.cursor_row(), 2);
927    assert_eq!(term.cursor_col(), 0);
928    term.set_cursor_row(1);
929    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930    term.set_cursor_row(3);
931    term.clear_cur_line();
932    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
933    assert_eq!(term.cursor_row(), 3);
934    assert_eq!(term.cursor_col(), 0);
935
936    term.append("Two lines above are intentionally left blank.\n");
937    assert_eq!(term.cursor_row(), 4);
938    assert_eq!(term.cursor_col(), 0);
939
940    // Set up the test pattern again, then play with insert/delete
941    term.append("0123456789\n");
942    term.append("ABCDEFGHIJ\n");
943    term.append("abcdefghij\n");
944    assert_eq!(term.cursor_row(), 7);
945
946    term.set_cursor_row(4);
947    term.set_cursor_col(4);
948    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
949
950    term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
951    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
952    term.cursor_right(5, false);
953    assert_eq!(term.cursor_col(), 9);
954    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
955
956    // Insert two blank rows above cursor. Cursor stays put.
957    term.insert_rows(2);
958    assert_eq!(term.cursor_row(), 4);
959    assert_eq!(term.cursor_col(), 9);
960    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
961    term.cursor_down(2, false); // Go down to find our text again
962    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
963
964    // Go back to the beginning of the inserted 'x' characters and delete them.
965    term.cursor_left(5);
966    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
967    term.delete_cur_chars(5);
968    assert_eq!(term.cursor_row(), 6);
969    assert_eq!(term.cursor_col(), 4);
970    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
971
972    term.delete_chars(7, 2, 2); // Delete "CD" from the next row
973    term.cursor_down(1, false);
974    term.cursor_left(2);
975    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
976
977    term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
978    assert_eq!(term.u8c_cursor().text_utf8(), b"c");
979    term.cursor_up(1, false);
980    term.delete_rows(2); // Delete remains of test pattern
981
982    term.set_text_attrib(Attrib::Bold);
983    term.insert_char_eol('-', 3, 15, 20);
984    term.set_cursor_row(3);
985    term.set_cursor_col(15);
986    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
987    assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
988
989    term.set_text_attrib(Attrib::Italic);
990    term.append(" and all lines below");
991    term.set_text_attrib(Attrib::Normal);
992    term.cursor_down(1, false);
993
994    let mut hsb = term.hscrollbar();
995    let mut sb = term.scrollbar();
996    hsb.set_value(100.0);
997    assert_eq!(hsb.value(), 100.0);
998    sb.set_value(50.0);
999    assert_eq!(sb.value(), 50.0);
1000    hsb.set_value(0.0);
1001    assert_eq!(hsb.value(), 0.0);
1002    sb.set_value(0.0);
1003    assert_eq!(sb.value(), 0.0);
1004}
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 977)
702fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
703    term.take_focus().unwrap();
704
705    // Test the attr_fg_color and attr_bg_color methods.
706    // Put a single character 'A' into the buffer and check it
707    term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
708    term.set_text_bg_color(Color::TransparentBg);
709    term.set_text_fg_color(Color::XtermWhite);
710    term.append("A");
711    let r = &term.u8c_disp_row(0);
712    let uc = r.col(0);
713    assert_eq!(uc.text_utf8(), b"A");
714    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
715    assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
716    assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
717    assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
718    assert_eq!(&uc.attrib(), &Attrib::Normal);
719
720    // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
721    term.clear();
722    term.set_text_fg_color_xterm(XtermColor::White);
723    term.set_text_bg_color_xterm(XtermColor::Black);
724    assert_eq!(term.text_attrib(), Attrib::Normal);
725
726    assert!(term.ansi());
727    term.append("B\x1b[32mC\x1b[1mD\n");
728
729    let r = &term.u8c_disp_row(0);
730    let uc = r.col(0);
731    assert_eq!(uc.text_utf8(), b"B");
732    assert!(uc.is_char(b'B'));
733    assert!(!uc.is_char(b'A'));
734    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
735    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
736    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
737    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
738    assert_eq!(
739        &uc.charflags(),
740        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
741    );
742
743    let uc = r.col(1);
744    assert_eq!(uc.text_utf8(), b"C");
745    assert!(uc.is_char(b'C'));
746    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
747    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
748    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
749    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
750    assert_eq!(
751        &uc.charflags(),
752        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
753    );
754
755    let uc = r.col(2);
756    assert_eq!(uc.text_utf8(), b"D");
757    assert!(uc.is_char(b'D'));
758    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
759    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
760    assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
761    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
762    assert_eq!(
763        &uc.attr_fgcolor(Some(term)),
764        &Color::from_rgb(0x20, 0xf0, 0x20)
765    );
766    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
767    assert_eq!(
768        &uc.charflags(),
769        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
770    );
771
772    // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
773    term.clear();
774    term.set_text_fg_color_xterm(XtermColor::White);
775    term.set_text_bg_color_xterm(XtermColor::Black);
776    term.set_text_attrib(Attrib::Normal);
777    assert_eq!(term.text_attrib(), Attrib::Normal);
778
779    assert!(term.ansi());
780    term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
781
782    let r = &term.u8c_disp_row(0);
783    let uc = r.col(0);
784    assert_eq!(uc.text_utf8(), b"B");
785    assert!(uc.is_char(b'B'));
786    assert!(!uc.is_char(b'A'));
787    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
788    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
789    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
790    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
791    assert_eq!(
792        &uc.charflags(),
793        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
794    );
795
796    let uc = r.col(1);
797    assert_eq!(uc.text_utf8(), b"C");
798    assert!(uc.is_char(b'C'));
799    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
800    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
801    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
802    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
803    assert_eq!(
804        &uc.charflags(),
805        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
806    );
807
808    let uc = r.col(2);
809    assert_eq!(uc.text_utf8(), b"D");
810    assert!(uc.is_char(b'D'));
811    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
812    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
813    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
814    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
815    assert_eq!(
816        &uc.charflags(),
817        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
818    );
819
820    let uc = r.col(3);
821    assert_eq!(uc.text_utf8(), b"E");
822    assert!(uc.is_char(b'E'));
823    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
824    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
825    assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
826    assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
827    assert_eq!(
828        &uc.charflags(),
829        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
830    );
831
832    // Test some miscellaneous Utf8 constants
833    assert_eq!(uc.length(), 1);
834    assert_eq!(uc.max_utf8(), 4);
835    assert_eq!(uc.pwidth(), 9.0);
836    assert_eq!(uc.pwidth_int(), 9);
837
838    term.set_text_fg_color_xterm(XtermColor::White);
839    term.set_text_bg_color_xterm(XtermColor::Black);
840    term.clear();
841    term.set_text_attrib(Attrib::Normal);
842
843    // Mouse selection functions
844    term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
845    term.clear_mouse_selection();
846    assert_eq!(term.get_selection(), None);
847
848    // Play with cursor position
849    term.append("0123456789\n"); // Set up test pattern
850    term.append("ABCDEFGHIJ\n");
851    term.append("abcdefghij\n");
852
853    term.set_cursor_row(1);
854    assert_eq!(term.cursor_row(), 1);
855    term.set_cursor_col(1);
856    assert_eq!(term.cursor_col(), 1);
857    assert_eq!(term.u8c_cursor().text_utf8(), b"1");
858
859    term.append("----"); // Overwrites text at cursor and moves cursor forward
860    assert_eq!(term.cursor_row(), 1);
861    assert_eq!(term.cursor_col(), 5);
862    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
863    term.set_cursor_col(1);
864    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
865
866    term.cursor_up(1, false);
867    assert_eq!(term.cursor_row(), 0);
868    assert_eq!(term.cursor_col(), 1);
869    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
870
871    // Hit top of screen, so nothing happens
872    term.cursor_up(1, false);
873    assert_eq!(term.cursor_row(), 0);
874    assert_eq!(term.cursor_col(), 1);
875    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
876
877    // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
878    term.cursor_up(1, true);
879    assert_eq!(term.cursor_row(), 0);
880    assert_eq!(term.cursor_col(), 1);
881    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
882
883    // Go back down to the overwritten text
884    term.cursor_down(2, false);
885    assert_eq!(term.cursor_row(), 2);
886    assert_eq!(term.cursor_col(), 1);
887    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
888
889    // Go right past the overwritten text
890    term.cursor_right(4, false);
891    assert_eq!(term.cursor_row(), 2);
892    assert_eq!(term.cursor_col(), 5);
893    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
894
895    // Go left to the end of the overwritten text
896    term.cursor_left(1);
897    assert_eq!(term.cursor_row(), 2);
898    assert_eq!(term.cursor_col(), 4);
899    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
900
901    // Scroll back down, removing the blank line at the top.
902    // Cursor stays in place, the text moves under it.
903    term.scroll(1);
904    assert_eq!(term.cursor_row(), 2);
905    assert_eq!(term.cursor_col(), 4);
906    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
907
908    // Clear from here to end-of-line
909    term.clear_eol();
910    assert_eq!(term.cursor_row(), 2);
911    assert_eq!(term.cursor_col(), 4);
912    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
913
914    // Now clear from here to start-of-line. Cursor does not move.
915    term.clear_sol();
916    assert_eq!(term.cursor_row(), 2);
917    assert_eq!(term.cursor_col(), 4);
918    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
919    term.cursor_left(1);
920    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
921    term.set_cursor_col(0);
922    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
923
924    // Clear some lines
925    term.clear_line(1);
926    assert_eq!(term.cursor_row(), 2);
927    assert_eq!(term.cursor_col(), 0);
928    term.set_cursor_row(1);
929    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930    term.set_cursor_row(3);
931    term.clear_cur_line();
932    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
933    assert_eq!(term.cursor_row(), 3);
934    assert_eq!(term.cursor_col(), 0);
935
936    term.append("Two lines above are intentionally left blank.\n");
937    assert_eq!(term.cursor_row(), 4);
938    assert_eq!(term.cursor_col(), 0);
939
940    // Set up the test pattern again, then play with insert/delete
941    term.append("0123456789\n");
942    term.append("ABCDEFGHIJ\n");
943    term.append("abcdefghij\n");
944    assert_eq!(term.cursor_row(), 7);
945
946    term.set_cursor_row(4);
947    term.set_cursor_col(4);
948    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
949
950    term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
951    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
952    term.cursor_right(5, false);
953    assert_eq!(term.cursor_col(), 9);
954    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
955
956    // Insert two blank rows above cursor. Cursor stays put.
957    term.insert_rows(2);
958    assert_eq!(term.cursor_row(), 4);
959    assert_eq!(term.cursor_col(), 9);
960    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
961    term.cursor_down(2, false); // Go down to find our text again
962    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
963
964    // Go back to the beginning of the inserted 'x' characters and delete them.
965    term.cursor_left(5);
966    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
967    term.delete_cur_chars(5);
968    assert_eq!(term.cursor_row(), 6);
969    assert_eq!(term.cursor_col(), 4);
970    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
971
972    term.delete_chars(7, 2, 2); // Delete "CD" from the next row
973    term.cursor_down(1, false);
974    term.cursor_left(2);
975    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
976
977    term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
978    assert_eq!(term.u8c_cursor().text_utf8(), b"c");
979    term.cursor_up(1, false);
980    term.delete_rows(2); // Delete remains of test pattern
981
982    term.set_text_attrib(Attrib::Bold);
983    term.insert_char_eol('-', 3, 15, 20);
984    term.set_cursor_row(3);
985    term.set_cursor_col(15);
986    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
987    assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
988
989    term.set_text_attrib(Attrib::Italic);
990    term.append(" and all lines below");
991    term.set_text_attrib(Attrib::Normal);
992    term.cursor_down(1, false);
993
994    let mut hsb = term.hscrollbar();
995    let mut sb = term.scrollbar();
996    hsb.set_value(100.0);
997    assert_eq!(hsb.value(), 100.0);
998    sb.set_value(50.0);
999    assert_eq!(sb.value(), 50.0);
1000    hsb.set_value(0.0);
1001    assert_eq!(hsb.value(), 0.0);
1002    sb.set_value(0.0);
1003    assert_eq!(sb.value(), 0.0);
1004}
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 152)
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!(term.text(true), "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");
115
116            // Testing ansi() and set_ansi() methods
117            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
118            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
119            term.set_ansi(false);
120            assert!(!term.ansi());
121            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
122            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
123            term.append_u8(b"Appending u8 array\n");
124            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
125            term.set_ansi(true); // Restore ANSI state
126
127            // Play with the horizontal scrollbar
128            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
129            term.set_hscrollbar_style(ScrollbarStyle::ON);
130            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
131
132            // Test show_unknown() as incidental part of testing append methods
133            term.set_show_unknown(true);
134            assert!(term.show_unknown());
135            term.append_ascii(
136                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
137            );
138            term.set_show_unknown(false);
139            assert!(!term.show_unknown());
140
141            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
142            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
143
144            let r = term.cursor_row();
145            assert_eq!(term.cursor_col(), 0);
146            term.append(&format!("Testing cursor row/col {r}"));
147            assert_eq!(term.cursor_col(), 24);
148            assert_eq!(term.cursor_row(), r);
149
150            // Test cursor color methods
151            assert_eq!(
152                term.cursor_bg_color(),
153                Color::XtermGreen,
154                "Default cursor bg at startup"
155            );
156            assert_eq!(
157                term.cursor_fg_color(),
158                Color::from_hex(0xff_ff_f0),
159                "Default cursor fg at startup"
160            );
161            term.set_cursor_bg_color(Color::Red);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
164            term.set_cursor_fg_color(Color::Blue);
165            assert_eq!(term.cursor_bg_color(), Color::Red);
166            assert_eq!(term.cursor_fg_color(), Color::Blue);
167            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
168            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
169            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
170            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
171
172            // The default display_rows() will derive from the window size
173            let dr = term.display_rows();
174            let height = term.height();
175            assert_eq!(height, term.h());
176            assert!(dr > 20, "Default display_rows at startup");
177            term.resize(term.x(), term.y(), term.w(), height * 2);
178            assert_eq!(term.h(), height * 2);
179            assert_eq!(height * 2, term.h());
180            assert!(term.display_rows() > dr);
181            term.resize(term.x(), term.y(), term.w(), height);
182
183            // The default display_columns() will derive from the window size
184            let dc = term.display_columns();
185            assert!(dc > 80, "Default display_rows at startup");
186            term.set_display_columns(200);
187            assert_eq!(term.display_columns(), 200);
188            term.append("\n         1         2         3         4         5         6         7         8         9");
189            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
190            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
191            term.set_display_columns(90);
192            assert_eq!(term.display_columns(), 90);
193            term.set_display_columns(dc); // Set back to default
194            assert_eq!(term.display_columns(), dc);
195
196            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
197            term.set_history_rows(50);
198            assert_eq!(term.history_rows(), 50);
199            term.set_history_rows(100); // Set back to default
200            assert_eq!(term.history_rows(), 100);
201
202            let hu = term.history_use();
203            term.append(&format!(
204                "history_use = {hu} (it's not clear what this means)\n"
205            ));
206            // assert_eq!(term.history_use(), hu+1);
207
208            term.append(&format!(
209                "margins = b:{} l:{} r:{} t{}\n",
210                term.margin_bottom(),
211                term.margin_left(),
212                term.margin_right(),
213                term.margin_top()
214            ));
215            assert_eq!(term.margin_bottom(), 3);
216            assert_eq!(term.margin_left(), 3);
217            assert_eq!(term.margin_right(), 3);
218            assert_eq!(term.margin_top(), 3);
219
220            term.set_margin_bottom(5);
221            term.set_margin_left(10);
222            term.set_margin_right(15);
223            term.set_margin_top(20);
224            assert_eq!(term.margin_bottom(), 5);
225            assert_eq!(term.margin_left(), 10);
226            assert_eq!(term.margin_right(), 15);
227            assert_eq!(term.margin_top(), 20);
228
229            term.append("Single character: '");
230            term.print_char('X');
231            term.append("', single UTF-8 character: '");
232            term.print_char_utf8('↑');
233            term.append("'\n");
234
235            let rr = term.redraw_rate();
236            assert_eq!(rr, 0.1, "Default redraw rate at startup");
237            term.append(&format!("Redraw rate {rr}\n"));
238            term.set_redraw_rate(1.0);
239            assert_eq!(term.redraw_rate(), 1.0);
240            term.set_redraw_rate(rr);
241            assert_eq!(term.redraw_rate(), rr);
242
243            let rs = term.redraw_style();
244            term.append(&format!("Redraw style {rs:?}\n"));
245            assert_eq!(
246                rs,
247                RedrawStyle::RateLimited,
248                "Default redraw style at startup"
249            );
250            term.set_redraw_style(RedrawStyle::NoRedraw);
251            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
252            term.set_redraw_style(rs);
253            assert_eq!(term.redraw_style(), rs);
254
255            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
256            assert_eq!(
257                RedrawStyle::NoRedraw.bits(),
258                0x0000,
259                "RedrawStyle enum values have been reassigned"
260            );
261            assert_eq!(
262                RedrawStyle::RateLimited.bits(),
263                0x0001,
264                "RedrawStyle enum values have been reassigned"
265            );
266            assert_eq!(
267                RedrawStyle::PerWrite.bits(),
268                0x0002,
269                "RedrawStyle enum values have been reassigned"
270            );
271
272            let sb = term.scrollbar();
273            let hsb = term.hscrollbar();
274            // Both vertical and horizontal scrollbars are at zero
275            assert_eq!(sb.value(), 0.0);
276            assert_eq!(hsb.value(), 0.0);
277            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
278
279            term.append(&format!(
280                "Scrollbar actual size {}\n",
281                term.scrollbar_actual_size()
282            ));
283            assert_eq!(term.scrollbar_actual_size(), 16);
284            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
285            assert_eq!(
286                term.scrollbar_size(),
287                0,
288                "Default scrollbar size at startup"
289            );
290            term.set_scrollbar_size(40);
291            assert_eq!(term.scrollbar_size(), 40);
292            assert_eq!(term.scrollbar_actual_size(), 40);
293            term.append(&format!(
294                "Scrollbar actual size {}\n",
295                term.scrollbar_actual_size()
296            ));
297            term.set_scrollbar_size(0); // Restore default
298            assert_eq!(term.scrollbar_size(), 0);
299            assert_eq!(term.scrollbar_actual_size(), 16);
300
301            let sfc = term.selection_fg_color();
302            let sbc = term.selection_bg_color();
303            assert_eq!(sfc, Color::Black);
304            assert_eq!(sbc, Color::White);
305            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
306            term.set_selection_fg_color(Color::Green);
307            term.set_selection_bg_color(Color::DarkBlue);
308            assert_eq!(term.selection_fg_color(), Color::Green);
309            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
310            term.set_selection_fg_color(sfc);
311            term.set_selection_bg_color(sbc);
312            assert_eq!(term.selection_fg_color(), Color::Black);
313            assert_eq!(term.selection_bg_color(), Color::White);
314
315            let tfcd = term.text_fg_color_default();
316            let tbcd = term.text_bg_color_default();
317            assert_eq!(tfcd, Color::XtermWhite);
318            assert_eq!(tbcd, Color::TransparentBg);
319            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
320            term.set_text_fg_color_default(Color::Green);
321            term.set_text_bg_color_default(Color::DarkBlue);
322            assert_eq!(term.text_fg_color_default(), Color::Green);
323            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
324            term.set_text_fg_color_default(tfcd);
325            term.set_text_bg_color_default(tbcd);
326            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
327            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
328
329            let tfc = term.text_fg_color();
330            let tbc = term.text_bg_color();
331            assert_eq!(tfc, Color::XtermWhite);
332            assert_eq!(tbc, Color::TransparentBg);
333            term.append(&format!("Text colors: {sfc} {sbc}\n"));
334            term.set_text_fg_color(Color::Green);
335            term.set_text_bg_color(Color::DarkBlue);
336            assert_eq!(term.text_fg_color(), Color::Green);
337            assert_eq!(term.text_bg_color(), Color::DarkBlue);
338            term.set_text_fg_color(tfc);
339            term.set_text_bg_color(tbc);
340            assert_eq!(term.text_fg_color(), Color::XtermWhite);
341            assert_eq!(term.text_bg_color(), Color::TransparentBg);
342
343            let tf = term.text_font();
344            term.append(&format!("Text font: {tf:?}\n"));
345            assert_eq!(tf, Font::Courier);
346            term.set_text_font(Font::Screen);
347            assert_eq!(term.text_font(), Font::Screen);
348            term.set_text_font(tf);
349            assert_eq!(term.text_font(), Font::Courier);
350
351            let ts = term.text_size();
352            let r = term.h_to_row(100);
353            let c = term.w_to_col(100);
354            term.append(&format!(
355                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
356            ));
357            assert_eq!(ts, 14);
358            term.set_text_size(30);
359            assert_eq!(term.text_size(), 30);
360            term.append(&format!(
361                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
362                term.text_size(),
363                term.h_to_row(100),
364                term.w_to_col(100)
365            ));
366            term.set_text_size(ts);
367            assert_eq!(term.text_size(), ts);
368            term.append(&format!(
369                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
370                term.text_size(),
371                term.h_to_row(100),
372                term.w_to_col(100)
373            ));
374
375            // Keyboard handler
376            term.handle({
377                move |term, e| {
378                    match e {
379                        fltk::enums::Event::KeyDown
380                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
381                        {
382                            // false to let FLTK handle ESC. true to hide ESC
383                            false
384                        }
385
386                        fltk::enums::Event::KeyDown
387                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
388                        {
389                            // We handle control keystroke
390                            let k = fltk::app::event_text();
391                            term.append_utf8(&k);
392                            true
393                        }
394
395                        fltk::enums::Event::KeyDown
396                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
397                        {
398                            // We handle normal printable keystroke
399                            let k = fltk::app::event_text();
400                            term.take_focus().unwrap();
401                            term.append(&k);
402                            true
403                        }
404
405                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
406                        // We can do this, or else ignore them (return false)
407                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
408                        //     term.redraw();
409                        //     true
410                        // }
411                        _ => false, // Let FLTK handle everything else
412                    }
413                }
414            });
415
416            let attr_save = term.text_attrib();
417            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
418            term.append("\nStartup tests complete. Keyboard is live.\n");
419            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
420            term.set_text_attrib(attr_save);
421            assert_eq!(term.text_attrib(), attr_save);
422            term.redraw();
423        }
424    });
425
426    app.run().unwrap();
427}
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 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!(term.text(true), "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");
115
116            // Testing ansi() and set_ansi() methods
117            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
118            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
119            term.set_ansi(false);
120            assert!(!term.ansi());
121            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
122            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
123            term.append_u8(b"Appending u8 array\n");
124            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
125            term.set_ansi(true); // Restore ANSI state
126
127            // Play with the horizontal scrollbar
128            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
129            term.set_hscrollbar_style(ScrollbarStyle::ON);
130            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
131
132            // Test show_unknown() as incidental part of testing append methods
133            term.set_show_unknown(true);
134            assert!(term.show_unknown());
135            term.append_ascii(
136                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
137            );
138            term.set_show_unknown(false);
139            assert!(!term.show_unknown());
140
141            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
142            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
143
144            let r = term.cursor_row();
145            assert_eq!(term.cursor_col(), 0);
146            term.append(&format!("Testing cursor row/col {r}"));
147            assert_eq!(term.cursor_col(), 24);
148            assert_eq!(term.cursor_row(), r);
149
150            // Test cursor color methods
151            assert_eq!(
152                term.cursor_bg_color(),
153                Color::XtermGreen,
154                "Default cursor bg at startup"
155            );
156            assert_eq!(
157                term.cursor_fg_color(),
158                Color::from_hex(0xff_ff_f0),
159                "Default cursor fg at startup"
160            );
161            term.set_cursor_bg_color(Color::Red);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
164            term.set_cursor_fg_color(Color::Blue);
165            assert_eq!(term.cursor_bg_color(), Color::Red);
166            assert_eq!(term.cursor_fg_color(), Color::Blue);
167            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
168            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
169            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
170            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
171
172            // The default display_rows() will derive from the window size
173            let dr = term.display_rows();
174            let height = term.height();
175            assert_eq!(height, term.h());
176            assert!(dr > 20, "Default display_rows at startup");
177            term.resize(term.x(), term.y(), term.w(), height * 2);
178            assert_eq!(term.h(), height * 2);
179            assert_eq!(height * 2, term.h());
180            assert!(term.display_rows() > dr);
181            term.resize(term.x(), term.y(), term.w(), height);
182
183            // The default display_columns() will derive from the window size
184            let dc = term.display_columns();
185            assert!(dc > 80, "Default display_rows at startup");
186            term.set_display_columns(200);
187            assert_eq!(term.display_columns(), 200);
188            term.append("\n         1         2         3         4         5         6         7         8         9");
189            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
190            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
191            term.set_display_columns(90);
192            assert_eq!(term.display_columns(), 90);
193            term.set_display_columns(dc); // Set back to default
194            assert_eq!(term.display_columns(), dc);
195
196            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
197            term.set_history_rows(50);
198            assert_eq!(term.history_rows(), 50);
199            term.set_history_rows(100); // Set back to default
200            assert_eq!(term.history_rows(), 100);
201
202            let hu = term.history_use();
203            term.append(&format!(
204                "history_use = {hu} (it's not clear what this means)\n"
205            ));
206            // assert_eq!(term.history_use(), hu+1);
207
208            term.append(&format!(
209                "margins = b:{} l:{} r:{} t{}\n",
210                term.margin_bottom(),
211                term.margin_left(),
212                term.margin_right(),
213                term.margin_top()
214            ));
215            assert_eq!(term.margin_bottom(), 3);
216            assert_eq!(term.margin_left(), 3);
217            assert_eq!(term.margin_right(), 3);
218            assert_eq!(term.margin_top(), 3);
219
220            term.set_margin_bottom(5);
221            term.set_margin_left(10);
222            term.set_margin_right(15);
223            term.set_margin_top(20);
224            assert_eq!(term.margin_bottom(), 5);
225            assert_eq!(term.margin_left(), 10);
226            assert_eq!(term.margin_right(), 15);
227            assert_eq!(term.margin_top(), 20);
228
229            term.append("Single character: '");
230            term.print_char('X');
231            term.append("', single UTF-8 character: '");
232            term.print_char_utf8('↑');
233            term.append("'\n");
234
235            let rr = term.redraw_rate();
236            assert_eq!(rr, 0.1, "Default redraw rate at startup");
237            term.append(&format!("Redraw rate {rr}\n"));
238            term.set_redraw_rate(1.0);
239            assert_eq!(term.redraw_rate(), 1.0);
240            term.set_redraw_rate(rr);
241            assert_eq!(term.redraw_rate(), rr);
242
243            let rs = term.redraw_style();
244            term.append(&format!("Redraw style {rs:?}\n"));
245            assert_eq!(
246                rs,
247                RedrawStyle::RateLimited,
248                "Default redraw style at startup"
249            );
250            term.set_redraw_style(RedrawStyle::NoRedraw);
251            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
252            term.set_redraw_style(rs);
253            assert_eq!(term.redraw_style(), rs);
254
255            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
256            assert_eq!(
257                RedrawStyle::NoRedraw.bits(),
258                0x0000,
259                "RedrawStyle enum values have been reassigned"
260            );
261            assert_eq!(
262                RedrawStyle::RateLimited.bits(),
263                0x0001,
264                "RedrawStyle enum values have been reassigned"
265            );
266            assert_eq!(
267                RedrawStyle::PerWrite.bits(),
268                0x0002,
269                "RedrawStyle enum values have been reassigned"
270            );
271
272            let sb = term.scrollbar();
273            let hsb = term.hscrollbar();
274            // Both vertical and horizontal scrollbars are at zero
275            assert_eq!(sb.value(), 0.0);
276            assert_eq!(hsb.value(), 0.0);
277            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
278
279            term.append(&format!(
280                "Scrollbar actual size {}\n",
281                term.scrollbar_actual_size()
282            ));
283            assert_eq!(term.scrollbar_actual_size(), 16);
284            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
285            assert_eq!(
286                term.scrollbar_size(),
287                0,
288                "Default scrollbar size at startup"
289            );
290            term.set_scrollbar_size(40);
291            assert_eq!(term.scrollbar_size(), 40);
292            assert_eq!(term.scrollbar_actual_size(), 40);
293            term.append(&format!(
294                "Scrollbar actual size {}\n",
295                term.scrollbar_actual_size()
296            ));
297            term.set_scrollbar_size(0); // Restore default
298            assert_eq!(term.scrollbar_size(), 0);
299            assert_eq!(term.scrollbar_actual_size(), 16);
300
301            let sfc = term.selection_fg_color();
302            let sbc = term.selection_bg_color();
303            assert_eq!(sfc, Color::Black);
304            assert_eq!(sbc, Color::White);
305            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
306            term.set_selection_fg_color(Color::Green);
307            term.set_selection_bg_color(Color::DarkBlue);
308            assert_eq!(term.selection_fg_color(), Color::Green);
309            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
310            term.set_selection_fg_color(sfc);
311            term.set_selection_bg_color(sbc);
312            assert_eq!(term.selection_fg_color(), Color::Black);
313            assert_eq!(term.selection_bg_color(), Color::White);
314
315            let tfcd = term.text_fg_color_default();
316            let tbcd = term.text_bg_color_default();
317            assert_eq!(tfcd, Color::XtermWhite);
318            assert_eq!(tbcd, Color::TransparentBg);
319            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
320            term.set_text_fg_color_default(Color::Green);
321            term.set_text_bg_color_default(Color::DarkBlue);
322            assert_eq!(term.text_fg_color_default(), Color::Green);
323            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
324            term.set_text_fg_color_default(tfcd);
325            term.set_text_bg_color_default(tbcd);
326            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
327            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
328
329            let tfc = term.text_fg_color();
330            let tbc = term.text_bg_color();
331            assert_eq!(tfc, Color::XtermWhite);
332            assert_eq!(tbc, Color::TransparentBg);
333            term.append(&format!("Text colors: {sfc} {sbc}\n"));
334            term.set_text_fg_color(Color::Green);
335            term.set_text_bg_color(Color::DarkBlue);
336            assert_eq!(term.text_fg_color(), Color::Green);
337            assert_eq!(term.text_bg_color(), Color::DarkBlue);
338            term.set_text_fg_color(tfc);
339            term.set_text_bg_color(tbc);
340            assert_eq!(term.text_fg_color(), Color::XtermWhite);
341            assert_eq!(term.text_bg_color(), Color::TransparentBg);
342
343            let tf = term.text_font();
344            term.append(&format!("Text font: {tf:?}\n"));
345            assert_eq!(tf, Font::Courier);
346            term.set_text_font(Font::Screen);
347            assert_eq!(term.text_font(), Font::Screen);
348            term.set_text_font(tf);
349            assert_eq!(term.text_font(), Font::Courier);
350
351            let ts = term.text_size();
352            let r = term.h_to_row(100);
353            let c = term.w_to_col(100);
354            term.append(&format!(
355                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
356            ));
357            assert_eq!(ts, 14);
358            term.set_text_size(30);
359            assert_eq!(term.text_size(), 30);
360            term.append(&format!(
361                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
362                term.text_size(),
363                term.h_to_row(100),
364                term.w_to_col(100)
365            ));
366            term.set_text_size(ts);
367            assert_eq!(term.text_size(), ts);
368            term.append(&format!(
369                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
370                term.text_size(),
371                term.h_to_row(100),
372                term.w_to_col(100)
373            ));
374
375            // Keyboard handler
376            term.handle({
377                move |term, e| {
378                    match e {
379                        fltk::enums::Event::KeyDown
380                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
381                        {
382                            // false to let FLTK handle ESC. true to hide ESC
383                            false
384                        }
385
386                        fltk::enums::Event::KeyDown
387                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
388                        {
389                            // We handle control keystroke
390                            let k = fltk::app::event_text();
391                            term.append_utf8(&k);
392                            true
393                        }
394
395                        fltk::enums::Event::KeyDown
396                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
397                        {
398                            // We handle normal printable keystroke
399                            let k = fltk::app::event_text();
400                            term.take_focus().unwrap();
401                            term.append(&k);
402                            true
403                        }
404
405                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
406                        // We can do this, or else ignore them (return false)
407                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
408                        //     term.redraw();
409                        //     true
410                        // }
411                        _ => false, // Let FLTK handle everything else
412                    }
413                }
414            });
415
416            let attr_save = term.text_attrib();
417            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
418            term.append("\nStartup tests complete. Keyboard is live.\n");
419            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
420            term.set_text_attrib(attr_save);
421            assert_eq!(term.text_attrib(), attr_save);
422            term.redraw();
423        }
424    });
425
426    app.run().unwrap();
427}
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 157)
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!(term.text(true), "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");
115
116            // Testing ansi() and set_ansi() methods
117            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
118            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
119            term.set_ansi(false);
120            assert!(!term.ansi());
121            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
122            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
123            term.append_u8(b"Appending u8 array\n");
124            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
125            term.set_ansi(true); // Restore ANSI state
126
127            // Play with the horizontal scrollbar
128            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
129            term.set_hscrollbar_style(ScrollbarStyle::ON);
130            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
131
132            // Test show_unknown() as incidental part of testing append methods
133            term.set_show_unknown(true);
134            assert!(term.show_unknown());
135            term.append_ascii(
136                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
137            );
138            term.set_show_unknown(false);
139            assert!(!term.show_unknown());
140
141            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
142            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
143
144            let r = term.cursor_row();
145            assert_eq!(term.cursor_col(), 0);
146            term.append(&format!("Testing cursor row/col {r}"));
147            assert_eq!(term.cursor_col(), 24);
148            assert_eq!(term.cursor_row(), r);
149
150            // Test cursor color methods
151            assert_eq!(
152                term.cursor_bg_color(),
153                Color::XtermGreen,
154                "Default cursor bg at startup"
155            );
156            assert_eq!(
157                term.cursor_fg_color(),
158                Color::from_hex(0xff_ff_f0),
159                "Default cursor fg at startup"
160            );
161            term.set_cursor_bg_color(Color::Red);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
164            term.set_cursor_fg_color(Color::Blue);
165            assert_eq!(term.cursor_bg_color(), Color::Red);
166            assert_eq!(term.cursor_fg_color(), Color::Blue);
167            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
168            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
169            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
170            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
171
172            // The default display_rows() will derive from the window size
173            let dr = term.display_rows();
174            let height = term.height();
175            assert_eq!(height, term.h());
176            assert!(dr > 20, "Default display_rows at startup");
177            term.resize(term.x(), term.y(), term.w(), height * 2);
178            assert_eq!(term.h(), height * 2);
179            assert_eq!(height * 2, term.h());
180            assert!(term.display_rows() > dr);
181            term.resize(term.x(), term.y(), term.w(), height);
182
183            // The default display_columns() will derive from the window size
184            let dc = term.display_columns();
185            assert!(dc > 80, "Default display_rows at startup");
186            term.set_display_columns(200);
187            assert_eq!(term.display_columns(), 200);
188            term.append("\n         1         2         3         4         5         6         7         8         9");
189            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
190            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
191            term.set_display_columns(90);
192            assert_eq!(term.display_columns(), 90);
193            term.set_display_columns(dc); // Set back to default
194            assert_eq!(term.display_columns(), dc);
195
196            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
197            term.set_history_rows(50);
198            assert_eq!(term.history_rows(), 50);
199            term.set_history_rows(100); // Set back to default
200            assert_eq!(term.history_rows(), 100);
201
202            let hu = term.history_use();
203            term.append(&format!(
204                "history_use = {hu} (it's not clear what this means)\n"
205            ));
206            // assert_eq!(term.history_use(), hu+1);
207
208            term.append(&format!(
209                "margins = b:{} l:{} r:{} t{}\n",
210                term.margin_bottom(),
211                term.margin_left(),
212                term.margin_right(),
213                term.margin_top()
214            ));
215            assert_eq!(term.margin_bottom(), 3);
216            assert_eq!(term.margin_left(), 3);
217            assert_eq!(term.margin_right(), 3);
218            assert_eq!(term.margin_top(), 3);
219
220            term.set_margin_bottom(5);
221            term.set_margin_left(10);
222            term.set_margin_right(15);
223            term.set_margin_top(20);
224            assert_eq!(term.margin_bottom(), 5);
225            assert_eq!(term.margin_left(), 10);
226            assert_eq!(term.margin_right(), 15);
227            assert_eq!(term.margin_top(), 20);
228
229            term.append("Single character: '");
230            term.print_char('X');
231            term.append("', single UTF-8 character: '");
232            term.print_char_utf8('↑');
233            term.append("'\n");
234
235            let rr = term.redraw_rate();
236            assert_eq!(rr, 0.1, "Default redraw rate at startup");
237            term.append(&format!("Redraw rate {rr}\n"));
238            term.set_redraw_rate(1.0);
239            assert_eq!(term.redraw_rate(), 1.0);
240            term.set_redraw_rate(rr);
241            assert_eq!(term.redraw_rate(), rr);
242
243            let rs = term.redraw_style();
244            term.append(&format!("Redraw style {rs:?}\n"));
245            assert_eq!(
246                rs,
247                RedrawStyle::RateLimited,
248                "Default redraw style at startup"
249            );
250            term.set_redraw_style(RedrawStyle::NoRedraw);
251            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
252            term.set_redraw_style(rs);
253            assert_eq!(term.redraw_style(), rs);
254
255            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
256            assert_eq!(
257                RedrawStyle::NoRedraw.bits(),
258                0x0000,
259                "RedrawStyle enum values have been reassigned"
260            );
261            assert_eq!(
262                RedrawStyle::RateLimited.bits(),
263                0x0001,
264                "RedrawStyle enum values have been reassigned"
265            );
266            assert_eq!(
267                RedrawStyle::PerWrite.bits(),
268                0x0002,
269                "RedrawStyle enum values have been reassigned"
270            );
271
272            let sb = term.scrollbar();
273            let hsb = term.hscrollbar();
274            // Both vertical and horizontal scrollbars are at zero
275            assert_eq!(sb.value(), 0.0);
276            assert_eq!(hsb.value(), 0.0);
277            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
278
279            term.append(&format!(
280                "Scrollbar actual size {}\n",
281                term.scrollbar_actual_size()
282            ));
283            assert_eq!(term.scrollbar_actual_size(), 16);
284            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
285            assert_eq!(
286                term.scrollbar_size(),
287                0,
288                "Default scrollbar size at startup"
289            );
290            term.set_scrollbar_size(40);
291            assert_eq!(term.scrollbar_size(), 40);
292            assert_eq!(term.scrollbar_actual_size(), 40);
293            term.append(&format!(
294                "Scrollbar actual size {}\n",
295                term.scrollbar_actual_size()
296            ));
297            term.set_scrollbar_size(0); // Restore default
298            assert_eq!(term.scrollbar_size(), 0);
299            assert_eq!(term.scrollbar_actual_size(), 16);
300
301            let sfc = term.selection_fg_color();
302            let sbc = term.selection_bg_color();
303            assert_eq!(sfc, Color::Black);
304            assert_eq!(sbc, Color::White);
305            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
306            term.set_selection_fg_color(Color::Green);
307            term.set_selection_bg_color(Color::DarkBlue);
308            assert_eq!(term.selection_fg_color(), Color::Green);
309            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
310            term.set_selection_fg_color(sfc);
311            term.set_selection_bg_color(sbc);
312            assert_eq!(term.selection_fg_color(), Color::Black);
313            assert_eq!(term.selection_bg_color(), Color::White);
314
315            let tfcd = term.text_fg_color_default();
316            let tbcd = term.text_bg_color_default();
317            assert_eq!(tfcd, Color::XtermWhite);
318            assert_eq!(tbcd, Color::TransparentBg);
319            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
320            term.set_text_fg_color_default(Color::Green);
321            term.set_text_bg_color_default(Color::DarkBlue);
322            assert_eq!(term.text_fg_color_default(), Color::Green);
323            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
324            term.set_text_fg_color_default(tfcd);
325            term.set_text_bg_color_default(tbcd);
326            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
327            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
328
329            let tfc = term.text_fg_color();
330            let tbc = term.text_bg_color();
331            assert_eq!(tfc, Color::XtermWhite);
332            assert_eq!(tbc, Color::TransparentBg);
333            term.append(&format!("Text colors: {sfc} {sbc}\n"));
334            term.set_text_fg_color(Color::Green);
335            term.set_text_bg_color(Color::DarkBlue);
336            assert_eq!(term.text_fg_color(), Color::Green);
337            assert_eq!(term.text_bg_color(), Color::DarkBlue);
338            term.set_text_fg_color(tfc);
339            term.set_text_bg_color(tbc);
340            assert_eq!(term.text_fg_color(), Color::XtermWhite);
341            assert_eq!(term.text_bg_color(), Color::TransparentBg);
342
343            let tf = term.text_font();
344            term.append(&format!("Text font: {tf:?}\n"));
345            assert_eq!(tf, Font::Courier);
346            term.set_text_font(Font::Screen);
347            assert_eq!(term.text_font(), Font::Screen);
348            term.set_text_font(tf);
349            assert_eq!(term.text_font(), Font::Courier);
350
351            let ts = term.text_size();
352            let r = term.h_to_row(100);
353            let c = term.w_to_col(100);
354            term.append(&format!(
355                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
356            ));
357            assert_eq!(ts, 14);
358            term.set_text_size(30);
359            assert_eq!(term.text_size(), 30);
360            term.append(&format!(
361                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
362                term.text_size(),
363                term.h_to_row(100),
364                term.w_to_col(100)
365            ));
366            term.set_text_size(ts);
367            assert_eq!(term.text_size(), ts);
368            term.append(&format!(
369                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
370                term.text_size(),
371                term.h_to_row(100),
372                term.w_to_col(100)
373            ));
374
375            // Keyboard handler
376            term.handle({
377                move |term, e| {
378                    match e {
379                        fltk::enums::Event::KeyDown
380                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
381                        {
382                            // false to let FLTK handle ESC. true to hide ESC
383                            false
384                        }
385
386                        fltk::enums::Event::KeyDown
387                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
388                        {
389                            // We handle control keystroke
390                            let k = fltk::app::event_text();
391                            term.append_utf8(&k);
392                            true
393                        }
394
395                        fltk::enums::Event::KeyDown
396                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
397                        {
398                            // We handle normal printable keystroke
399                            let k = fltk::app::event_text();
400                            term.take_focus().unwrap();
401                            term.append(&k);
402                            true
403                        }
404
405                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
406                        // We can do this, or else ignore them (return false)
407                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
408                        //     term.redraw();
409                        //     true
410                        // }
411                        _ => false, // Let FLTK handle everything else
412                    }
413                }
414            });
415
416            let attr_save = term.text_attrib();
417            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
418            term.append("\nStartup tests complete. Keyboard is live.\n");
419            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
420            term.set_text_attrib(attr_save);
421            assert_eq!(term.text_attrib(), attr_save);
422            term.redraw();
423        }
424    });
425
426    app.run().unwrap();
427}
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 164)
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!(term.text(true), "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");
115
116            // Testing ansi() and set_ansi() methods
117            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
118            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
119            term.set_ansi(false);
120            assert!(!term.ansi());
121            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
122            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
123            term.append_u8(b"Appending u8 array\n");
124            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
125            term.set_ansi(true); // Restore ANSI state
126
127            // Play with the horizontal scrollbar
128            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
129            term.set_hscrollbar_style(ScrollbarStyle::ON);
130            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
131
132            // Test show_unknown() as incidental part of testing append methods
133            term.set_show_unknown(true);
134            assert!(term.show_unknown());
135            term.append_ascii(
136                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
137            );
138            term.set_show_unknown(false);
139            assert!(!term.show_unknown());
140
141            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
142            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
143
144            let r = term.cursor_row();
145            assert_eq!(term.cursor_col(), 0);
146            term.append(&format!("Testing cursor row/col {r}"));
147            assert_eq!(term.cursor_col(), 24);
148            assert_eq!(term.cursor_row(), r);
149
150            // Test cursor color methods
151            assert_eq!(
152                term.cursor_bg_color(),
153                Color::XtermGreen,
154                "Default cursor bg at startup"
155            );
156            assert_eq!(
157                term.cursor_fg_color(),
158                Color::from_hex(0xff_ff_f0),
159                "Default cursor fg at startup"
160            );
161            term.set_cursor_bg_color(Color::Red);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
164            term.set_cursor_fg_color(Color::Blue);
165            assert_eq!(term.cursor_bg_color(), Color::Red);
166            assert_eq!(term.cursor_fg_color(), Color::Blue);
167            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
168            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
169            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
170            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
171
172            // The default display_rows() will derive from the window size
173            let dr = term.display_rows();
174            let height = term.height();
175            assert_eq!(height, term.h());
176            assert!(dr > 20, "Default display_rows at startup");
177            term.resize(term.x(), term.y(), term.w(), height * 2);
178            assert_eq!(term.h(), height * 2);
179            assert_eq!(height * 2, term.h());
180            assert!(term.display_rows() > dr);
181            term.resize(term.x(), term.y(), term.w(), height);
182
183            // The default display_columns() will derive from the window size
184            let dc = term.display_columns();
185            assert!(dc > 80, "Default display_rows at startup");
186            term.set_display_columns(200);
187            assert_eq!(term.display_columns(), 200);
188            term.append("\n         1         2         3         4         5         6         7         8         9");
189            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
190            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
191            term.set_display_columns(90);
192            assert_eq!(term.display_columns(), 90);
193            term.set_display_columns(dc); // Set back to default
194            assert_eq!(term.display_columns(), dc);
195
196            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
197            term.set_history_rows(50);
198            assert_eq!(term.history_rows(), 50);
199            term.set_history_rows(100); // Set back to default
200            assert_eq!(term.history_rows(), 100);
201
202            let hu = term.history_use();
203            term.append(&format!(
204                "history_use = {hu} (it's not clear what this means)\n"
205            ));
206            // assert_eq!(term.history_use(), hu+1);
207
208            term.append(&format!(
209                "margins = b:{} l:{} r:{} t{}\n",
210                term.margin_bottom(),
211                term.margin_left(),
212                term.margin_right(),
213                term.margin_top()
214            ));
215            assert_eq!(term.margin_bottom(), 3);
216            assert_eq!(term.margin_left(), 3);
217            assert_eq!(term.margin_right(), 3);
218            assert_eq!(term.margin_top(), 3);
219
220            term.set_margin_bottom(5);
221            term.set_margin_left(10);
222            term.set_margin_right(15);
223            term.set_margin_top(20);
224            assert_eq!(term.margin_bottom(), 5);
225            assert_eq!(term.margin_left(), 10);
226            assert_eq!(term.margin_right(), 15);
227            assert_eq!(term.margin_top(), 20);
228
229            term.append("Single character: '");
230            term.print_char('X');
231            term.append("', single UTF-8 character: '");
232            term.print_char_utf8('↑');
233            term.append("'\n");
234
235            let rr = term.redraw_rate();
236            assert_eq!(rr, 0.1, "Default redraw rate at startup");
237            term.append(&format!("Redraw rate {rr}\n"));
238            term.set_redraw_rate(1.0);
239            assert_eq!(term.redraw_rate(), 1.0);
240            term.set_redraw_rate(rr);
241            assert_eq!(term.redraw_rate(), rr);
242
243            let rs = term.redraw_style();
244            term.append(&format!("Redraw style {rs:?}\n"));
245            assert_eq!(
246                rs,
247                RedrawStyle::RateLimited,
248                "Default redraw style at startup"
249            );
250            term.set_redraw_style(RedrawStyle::NoRedraw);
251            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
252            term.set_redraw_style(rs);
253            assert_eq!(term.redraw_style(), rs);
254
255            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
256            assert_eq!(
257                RedrawStyle::NoRedraw.bits(),
258                0x0000,
259                "RedrawStyle enum values have been reassigned"
260            );
261            assert_eq!(
262                RedrawStyle::RateLimited.bits(),
263                0x0001,
264                "RedrawStyle enum values have been reassigned"
265            );
266            assert_eq!(
267                RedrawStyle::PerWrite.bits(),
268                0x0002,
269                "RedrawStyle enum values have been reassigned"
270            );
271
272            let sb = term.scrollbar();
273            let hsb = term.hscrollbar();
274            // Both vertical and horizontal scrollbars are at zero
275            assert_eq!(sb.value(), 0.0);
276            assert_eq!(hsb.value(), 0.0);
277            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
278
279            term.append(&format!(
280                "Scrollbar actual size {}\n",
281                term.scrollbar_actual_size()
282            ));
283            assert_eq!(term.scrollbar_actual_size(), 16);
284            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
285            assert_eq!(
286                term.scrollbar_size(),
287                0,
288                "Default scrollbar size at startup"
289            );
290            term.set_scrollbar_size(40);
291            assert_eq!(term.scrollbar_size(), 40);
292            assert_eq!(term.scrollbar_actual_size(), 40);
293            term.append(&format!(
294                "Scrollbar actual size {}\n",
295                term.scrollbar_actual_size()
296            ));
297            term.set_scrollbar_size(0); // Restore default
298            assert_eq!(term.scrollbar_size(), 0);
299            assert_eq!(term.scrollbar_actual_size(), 16);
300
301            let sfc = term.selection_fg_color();
302            let sbc = term.selection_bg_color();
303            assert_eq!(sfc, Color::Black);
304            assert_eq!(sbc, Color::White);
305            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
306            term.set_selection_fg_color(Color::Green);
307            term.set_selection_bg_color(Color::DarkBlue);
308            assert_eq!(term.selection_fg_color(), Color::Green);
309            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
310            term.set_selection_fg_color(sfc);
311            term.set_selection_bg_color(sbc);
312            assert_eq!(term.selection_fg_color(), Color::Black);
313            assert_eq!(term.selection_bg_color(), Color::White);
314
315            let tfcd = term.text_fg_color_default();
316            let tbcd = term.text_bg_color_default();
317            assert_eq!(tfcd, Color::XtermWhite);
318            assert_eq!(tbcd, Color::TransparentBg);
319            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
320            term.set_text_fg_color_default(Color::Green);
321            term.set_text_bg_color_default(Color::DarkBlue);
322            assert_eq!(term.text_fg_color_default(), Color::Green);
323            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
324            term.set_text_fg_color_default(tfcd);
325            term.set_text_bg_color_default(tbcd);
326            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
327            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
328
329            let tfc = term.text_fg_color();
330            let tbc = term.text_bg_color();
331            assert_eq!(tfc, Color::XtermWhite);
332            assert_eq!(tbc, Color::TransparentBg);
333            term.append(&format!("Text colors: {sfc} {sbc}\n"));
334            term.set_text_fg_color(Color::Green);
335            term.set_text_bg_color(Color::DarkBlue);
336            assert_eq!(term.text_fg_color(), Color::Green);
337            assert_eq!(term.text_bg_color(), Color::DarkBlue);
338            term.set_text_fg_color(tfc);
339            term.set_text_bg_color(tbc);
340            assert_eq!(term.text_fg_color(), Color::XtermWhite);
341            assert_eq!(term.text_bg_color(), Color::TransparentBg);
342
343            let tf = term.text_font();
344            term.append(&format!("Text font: {tf:?}\n"));
345            assert_eq!(tf, Font::Courier);
346            term.set_text_font(Font::Screen);
347            assert_eq!(term.text_font(), Font::Screen);
348            term.set_text_font(tf);
349            assert_eq!(term.text_font(), Font::Courier);
350
351            let ts = term.text_size();
352            let r = term.h_to_row(100);
353            let c = term.w_to_col(100);
354            term.append(&format!(
355                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
356            ));
357            assert_eq!(ts, 14);
358            term.set_text_size(30);
359            assert_eq!(term.text_size(), 30);
360            term.append(&format!(
361                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
362                term.text_size(),
363                term.h_to_row(100),
364                term.w_to_col(100)
365            ));
366            term.set_text_size(ts);
367            assert_eq!(term.text_size(), ts);
368            term.append(&format!(
369                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
370                term.text_size(),
371                term.h_to_row(100),
372                term.w_to_col(100)
373            ));
374
375            // Keyboard handler
376            term.handle({
377                move |term, e| {
378                    match e {
379                        fltk::enums::Event::KeyDown
380                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
381                        {
382                            // false to let FLTK handle ESC. true to hide ESC
383                            false
384                        }
385
386                        fltk::enums::Event::KeyDown
387                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
388                        {
389                            // We handle control keystroke
390                            let k = fltk::app::event_text();
391                            term.append_utf8(&k);
392                            true
393                        }
394
395                        fltk::enums::Event::KeyDown
396                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
397                        {
398                            // We handle normal printable keystroke
399                            let k = fltk::app::event_text();
400                            term.take_focus().unwrap();
401                            term.append(&k);
402                            true
403                        }
404
405                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
406                        // We can do this, or else ignore them (return false)
407                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
408                        //     term.redraw();
409                        //     true
410                        // }
411                        _ => false, // Let FLTK handle everything else
412                    }
413                }
414            });
415
416            let attr_save = term.text_attrib();
417            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
418            term.append("\nStartup tests complete. Keyboard is live.\n");
419            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
420            term.set_text_attrib(attr_save);
421            assert_eq!(term.text_attrib(), attr_save);
422            term.redraw();
423        }
424    });
425
426    app.run().unwrap();
427}
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 844)
702fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
703    term.take_focus().unwrap();
704
705    // Test the attr_fg_color and attr_bg_color methods.
706    // Put a single character 'A' into the buffer and check it
707    term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
708    term.set_text_bg_color(Color::TransparentBg);
709    term.set_text_fg_color(Color::XtermWhite);
710    term.append("A");
711    let r = &term.u8c_disp_row(0);
712    let uc = r.col(0);
713    assert_eq!(uc.text_utf8(), b"A");
714    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
715    assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
716    assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
717    assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
718    assert_eq!(&uc.attrib(), &Attrib::Normal);
719
720    // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
721    term.clear();
722    term.set_text_fg_color_xterm(XtermColor::White);
723    term.set_text_bg_color_xterm(XtermColor::Black);
724    assert_eq!(term.text_attrib(), Attrib::Normal);
725
726    assert!(term.ansi());
727    term.append("B\x1b[32mC\x1b[1mD\n");
728
729    let r = &term.u8c_disp_row(0);
730    let uc = r.col(0);
731    assert_eq!(uc.text_utf8(), b"B");
732    assert!(uc.is_char(b'B'));
733    assert!(!uc.is_char(b'A'));
734    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
735    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
736    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
737    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
738    assert_eq!(
739        &uc.charflags(),
740        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
741    );
742
743    let uc = r.col(1);
744    assert_eq!(uc.text_utf8(), b"C");
745    assert!(uc.is_char(b'C'));
746    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
747    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
748    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
749    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
750    assert_eq!(
751        &uc.charflags(),
752        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
753    );
754
755    let uc = r.col(2);
756    assert_eq!(uc.text_utf8(), b"D");
757    assert!(uc.is_char(b'D'));
758    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
759    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
760    assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
761    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
762    assert_eq!(
763        &uc.attr_fgcolor(Some(term)),
764        &Color::from_rgb(0x20, 0xf0, 0x20)
765    );
766    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
767    assert_eq!(
768        &uc.charflags(),
769        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
770    );
771
772    // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
773    term.clear();
774    term.set_text_fg_color_xterm(XtermColor::White);
775    term.set_text_bg_color_xterm(XtermColor::Black);
776    term.set_text_attrib(Attrib::Normal);
777    assert_eq!(term.text_attrib(), Attrib::Normal);
778
779    assert!(term.ansi());
780    term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
781
782    let r = &term.u8c_disp_row(0);
783    let uc = r.col(0);
784    assert_eq!(uc.text_utf8(), b"B");
785    assert!(uc.is_char(b'B'));
786    assert!(!uc.is_char(b'A'));
787    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
788    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
789    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
790    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
791    assert_eq!(
792        &uc.charflags(),
793        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
794    );
795
796    let uc = r.col(1);
797    assert_eq!(uc.text_utf8(), b"C");
798    assert!(uc.is_char(b'C'));
799    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
800    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
801    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
802    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
803    assert_eq!(
804        &uc.charflags(),
805        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
806    );
807
808    let uc = r.col(2);
809    assert_eq!(uc.text_utf8(), b"D");
810    assert!(uc.is_char(b'D'));
811    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
812    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
813    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
814    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
815    assert_eq!(
816        &uc.charflags(),
817        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
818    );
819
820    let uc = r.col(3);
821    assert_eq!(uc.text_utf8(), b"E");
822    assert!(uc.is_char(b'E'));
823    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
824    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
825    assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
826    assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
827    assert_eq!(
828        &uc.charflags(),
829        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
830    );
831
832    // Test some miscellaneous Utf8 constants
833    assert_eq!(uc.length(), 1);
834    assert_eq!(uc.max_utf8(), 4);
835    assert_eq!(uc.pwidth(), 9.0);
836    assert_eq!(uc.pwidth_int(), 9);
837
838    term.set_text_fg_color_xterm(XtermColor::White);
839    term.set_text_bg_color_xterm(XtermColor::Black);
840    term.clear();
841    term.set_text_attrib(Attrib::Normal);
842
843    // Mouse selection functions
844    term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
845    term.clear_mouse_selection();
846    assert_eq!(term.get_selection(), None);
847
848    // Play with cursor position
849    term.append("0123456789\n"); // Set up test pattern
850    term.append("ABCDEFGHIJ\n");
851    term.append("abcdefghij\n");
852
853    term.set_cursor_row(1);
854    assert_eq!(term.cursor_row(), 1);
855    term.set_cursor_col(1);
856    assert_eq!(term.cursor_col(), 1);
857    assert_eq!(term.u8c_cursor().text_utf8(), b"1");
858
859    term.append("----"); // Overwrites text at cursor and moves cursor forward
860    assert_eq!(term.cursor_row(), 1);
861    assert_eq!(term.cursor_col(), 5);
862    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
863    term.set_cursor_col(1);
864    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
865
866    term.cursor_up(1, false);
867    assert_eq!(term.cursor_row(), 0);
868    assert_eq!(term.cursor_col(), 1);
869    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
870
871    // Hit top of screen, so nothing happens
872    term.cursor_up(1, false);
873    assert_eq!(term.cursor_row(), 0);
874    assert_eq!(term.cursor_col(), 1);
875    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
876
877    // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
878    term.cursor_up(1, true);
879    assert_eq!(term.cursor_row(), 0);
880    assert_eq!(term.cursor_col(), 1);
881    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
882
883    // Go back down to the overwritten text
884    term.cursor_down(2, false);
885    assert_eq!(term.cursor_row(), 2);
886    assert_eq!(term.cursor_col(), 1);
887    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
888
889    // Go right past the overwritten text
890    term.cursor_right(4, false);
891    assert_eq!(term.cursor_row(), 2);
892    assert_eq!(term.cursor_col(), 5);
893    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
894
895    // Go left to the end of the overwritten text
896    term.cursor_left(1);
897    assert_eq!(term.cursor_row(), 2);
898    assert_eq!(term.cursor_col(), 4);
899    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
900
901    // Scroll back down, removing the blank line at the top.
902    // Cursor stays in place, the text moves under it.
903    term.scroll(1);
904    assert_eq!(term.cursor_row(), 2);
905    assert_eq!(term.cursor_col(), 4);
906    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
907
908    // Clear from here to end-of-line
909    term.clear_eol();
910    assert_eq!(term.cursor_row(), 2);
911    assert_eq!(term.cursor_col(), 4);
912    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
913
914    // Now clear from here to start-of-line. Cursor does not move.
915    term.clear_sol();
916    assert_eq!(term.cursor_row(), 2);
917    assert_eq!(term.cursor_col(), 4);
918    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
919    term.cursor_left(1);
920    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
921    term.set_cursor_col(0);
922    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
923
924    // Clear some lines
925    term.clear_line(1);
926    assert_eq!(term.cursor_row(), 2);
927    assert_eq!(term.cursor_col(), 0);
928    term.set_cursor_row(1);
929    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930    term.set_cursor_row(3);
931    term.clear_cur_line();
932    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
933    assert_eq!(term.cursor_row(), 3);
934    assert_eq!(term.cursor_col(), 0);
935
936    term.append("Two lines above are intentionally left blank.\n");
937    assert_eq!(term.cursor_row(), 4);
938    assert_eq!(term.cursor_col(), 0);
939
940    // Set up the test pattern again, then play with insert/delete
941    term.append("0123456789\n");
942    term.append("ABCDEFGHIJ\n");
943    term.append("abcdefghij\n");
944    assert_eq!(term.cursor_row(), 7);
945
946    term.set_cursor_row(4);
947    term.set_cursor_col(4);
948    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
949
950    term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
951    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
952    term.cursor_right(5, false);
953    assert_eq!(term.cursor_col(), 9);
954    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
955
956    // Insert two blank rows above cursor. Cursor stays put.
957    term.insert_rows(2);
958    assert_eq!(term.cursor_row(), 4);
959    assert_eq!(term.cursor_col(), 9);
960    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
961    term.cursor_down(2, false); // Go down to find our text again
962    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
963
964    // Go back to the beginning of the inserted 'x' characters and delete them.
965    term.cursor_left(5);
966    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
967    term.delete_cur_chars(5);
968    assert_eq!(term.cursor_row(), 6);
969    assert_eq!(term.cursor_col(), 4);
970    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
971
972    term.delete_chars(7, 2, 2); // Delete "CD" from the next row
973    term.cursor_down(1, false);
974    term.cursor_left(2);
975    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
976
977    term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
978    assert_eq!(term.u8c_cursor().text_utf8(), b"c");
979    term.cursor_up(1, false);
980    term.delete_rows(2); // Delete remains of test pattern
981
982    term.set_text_attrib(Attrib::Bold);
983    term.insert_char_eol('-', 3, 15, 20);
984    term.set_cursor_row(3);
985    term.set_cursor_col(15);
986    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
987    assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
988
989    term.set_text_attrib(Attrib::Italic);
990    term.append(" and all lines below");
991    term.set_text_attrib(Attrib::Normal);
992    term.cursor_down(1, false);
993
994    let mut hsb = term.hscrollbar();
995    let mut sb = term.scrollbar();
996    hsb.set_value(100.0);
997    assert_eq!(hsb.value(), 100.0);
998    sb.set_value(50.0);
999    assert_eq!(sb.value(), 50.0);
1000    hsb.set_value(0.0);
1001    assert_eq!(hsb.value(), 0.0);
1002    sb.set_value(0.0);
1003    assert_eq!(sb.value(), 0.0);
1004}
Source

pub fn cursor_home(&mut self)

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

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

pub fn display_rows(&self) -> i32

Return terminal’s display height in lines of text.

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

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

Set the bottom 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!(term.text(true), "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");
115
116            // Testing ansi() and set_ansi() methods
117            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
118            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
119            term.set_ansi(false);
120            assert!(!term.ansi());
121            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
122            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
123            term.append_u8(b"Appending u8 array\n");
124            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
125            term.set_ansi(true); // Restore ANSI state
126
127            // Play with the horizontal scrollbar
128            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
129            term.set_hscrollbar_style(ScrollbarStyle::ON);
130            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
131
132            // Test show_unknown() as incidental part of testing append methods
133            term.set_show_unknown(true);
134            assert!(term.show_unknown());
135            term.append_ascii(
136                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
137            );
138            term.set_show_unknown(false);
139            assert!(!term.show_unknown());
140
141            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
142            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
143
144            let r = term.cursor_row();
145            assert_eq!(term.cursor_col(), 0);
146            term.append(&format!("Testing cursor row/col {r}"));
147            assert_eq!(term.cursor_col(), 24);
148            assert_eq!(term.cursor_row(), r);
149
150            // Test cursor color methods
151            assert_eq!(
152                term.cursor_bg_color(),
153                Color::XtermGreen,
154                "Default cursor bg at startup"
155            );
156            assert_eq!(
157                term.cursor_fg_color(),
158                Color::from_hex(0xff_ff_f0),
159                "Default cursor fg at startup"
160            );
161            term.set_cursor_bg_color(Color::Red);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
164            term.set_cursor_fg_color(Color::Blue);
165            assert_eq!(term.cursor_bg_color(), Color::Red);
166            assert_eq!(term.cursor_fg_color(), Color::Blue);
167            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
168            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
169            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
170            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
171
172            // The default display_rows() will derive from the window size
173            let dr = term.display_rows();
174            let height = term.height();
175            assert_eq!(height, term.h());
176            assert!(dr > 20, "Default display_rows at startup");
177            term.resize(term.x(), term.y(), term.w(), height * 2);
178            assert_eq!(term.h(), height * 2);
179            assert_eq!(height * 2, term.h());
180            assert!(term.display_rows() > dr);
181            term.resize(term.x(), term.y(), term.w(), height);
182
183            // The default display_columns() will derive from the window size
184            let dc = term.display_columns();
185            assert!(dc > 80, "Default display_rows at startup");
186            term.set_display_columns(200);
187            assert_eq!(term.display_columns(), 200);
188            term.append("\n         1         2         3         4         5         6         7         8         9");
189            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
190            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
191            term.set_display_columns(90);
192            assert_eq!(term.display_columns(), 90);
193            term.set_display_columns(dc); // Set back to default
194            assert_eq!(term.display_columns(), dc);
195
196            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
197            term.set_history_rows(50);
198            assert_eq!(term.history_rows(), 50);
199            term.set_history_rows(100); // Set back to default
200            assert_eq!(term.history_rows(), 100);
201
202            let hu = term.history_use();
203            term.append(&format!(
204                "history_use = {hu} (it's not clear what this means)\n"
205            ));
206            // assert_eq!(term.history_use(), hu+1);
207
208            term.append(&format!(
209                "margins = b:{} l:{} r:{} t{}\n",
210                term.margin_bottom(),
211                term.margin_left(),
212                term.margin_right(),
213                term.margin_top()
214            ));
215            assert_eq!(term.margin_bottom(), 3);
216            assert_eq!(term.margin_left(), 3);
217            assert_eq!(term.margin_right(), 3);
218            assert_eq!(term.margin_top(), 3);
219
220            term.set_margin_bottom(5);
221            term.set_margin_left(10);
222            term.set_margin_right(15);
223            term.set_margin_top(20);
224            assert_eq!(term.margin_bottom(), 5);
225            assert_eq!(term.margin_left(), 10);
226            assert_eq!(term.margin_right(), 15);
227            assert_eq!(term.margin_top(), 20);
228
229            term.append("Single character: '");
230            term.print_char('X');
231            term.append("', single UTF-8 character: '");
232            term.print_char_utf8('↑');
233            term.append("'\n");
234
235            let rr = term.redraw_rate();
236            assert_eq!(rr, 0.1, "Default redraw rate at startup");
237            term.append(&format!("Redraw rate {rr}\n"));
238            term.set_redraw_rate(1.0);
239            assert_eq!(term.redraw_rate(), 1.0);
240            term.set_redraw_rate(rr);
241            assert_eq!(term.redraw_rate(), rr);
242
243            let rs = term.redraw_style();
244            term.append(&format!("Redraw style {rs:?}\n"));
245            assert_eq!(
246                rs,
247                RedrawStyle::RateLimited,
248                "Default redraw style at startup"
249            );
250            term.set_redraw_style(RedrawStyle::NoRedraw);
251            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
252            term.set_redraw_style(rs);
253            assert_eq!(term.redraw_style(), rs);
254
255            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
256            assert_eq!(
257                RedrawStyle::NoRedraw.bits(),
258                0x0000,
259                "RedrawStyle enum values have been reassigned"
260            );
261            assert_eq!(
262                RedrawStyle::RateLimited.bits(),
263                0x0001,
264                "RedrawStyle enum values have been reassigned"
265            );
266            assert_eq!(
267                RedrawStyle::PerWrite.bits(),
268                0x0002,
269                "RedrawStyle enum values have been reassigned"
270            );
271
272            let sb = term.scrollbar();
273            let hsb = term.hscrollbar();
274            // Both vertical and horizontal scrollbars are at zero
275            assert_eq!(sb.value(), 0.0);
276            assert_eq!(hsb.value(), 0.0);
277            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
278
279            term.append(&format!(
280                "Scrollbar actual size {}\n",
281                term.scrollbar_actual_size()
282            ));
283            assert_eq!(term.scrollbar_actual_size(), 16);
284            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
285            assert_eq!(
286                term.scrollbar_size(),
287                0,
288                "Default scrollbar size at startup"
289            );
290            term.set_scrollbar_size(40);
291            assert_eq!(term.scrollbar_size(), 40);
292            assert_eq!(term.scrollbar_actual_size(), 40);
293            term.append(&format!(
294                "Scrollbar actual size {}\n",
295                term.scrollbar_actual_size()
296            ));
297            term.set_scrollbar_size(0); // Restore default
298            assert_eq!(term.scrollbar_size(), 0);
299            assert_eq!(term.scrollbar_actual_size(), 16);
300
301            let sfc = term.selection_fg_color();
302            let sbc = term.selection_bg_color();
303            assert_eq!(sfc, Color::Black);
304            assert_eq!(sbc, Color::White);
305            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
306            term.set_selection_fg_color(Color::Green);
307            term.set_selection_bg_color(Color::DarkBlue);
308            assert_eq!(term.selection_fg_color(), Color::Green);
309            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
310            term.set_selection_fg_color(sfc);
311            term.set_selection_bg_color(sbc);
312            assert_eq!(term.selection_fg_color(), Color::Black);
313            assert_eq!(term.selection_bg_color(), Color::White);
314
315            let tfcd = term.text_fg_color_default();
316            let tbcd = term.text_bg_color_default();
317            assert_eq!(tfcd, Color::XtermWhite);
318            assert_eq!(tbcd, Color::TransparentBg);
319            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
320            term.set_text_fg_color_default(Color::Green);
321            term.set_text_bg_color_default(Color::DarkBlue);
322            assert_eq!(term.text_fg_color_default(), Color::Green);
323            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
324            term.set_text_fg_color_default(tfcd);
325            term.set_text_bg_color_default(tbcd);
326            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
327            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
328
329            let tfc = term.text_fg_color();
330            let tbc = term.text_bg_color();
331            assert_eq!(tfc, Color::XtermWhite);
332            assert_eq!(tbc, Color::TransparentBg);
333            term.append(&format!("Text colors: {sfc} {sbc}\n"));
334            term.set_text_fg_color(Color::Green);
335            term.set_text_bg_color(Color::DarkBlue);
336            assert_eq!(term.text_fg_color(), Color::Green);
337            assert_eq!(term.text_bg_color(), Color::DarkBlue);
338            term.set_text_fg_color(tfc);
339            term.set_text_bg_color(tbc);
340            assert_eq!(term.text_fg_color(), Color::XtermWhite);
341            assert_eq!(term.text_bg_color(), Color::TransparentBg);
342
343            let tf = term.text_font();
344            term.append(&format!("Text font: {tf:?}\n"));
345            assert_eq!(tf, Font::Courier);
346            term.set_text_font(Font::Screen);
347            assert_eq!(term.text_font(), Font::Screen);
348            term.set_text_font(tf);
349            assert_eq!(term.text_font(), Font::Courier);
350
351            let ts = term.text_size();
352            let r = term.h_to_row(100);
353            let c = term.w_to_col(100);
354            term.append(&format!(
355                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
356            ));
357            assert_eq!(ts, 14);
358            term.set_text_size(30);
359            assert_eq!(term.text_size(), 30);
360            term.append(&format!(
361                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
362                term.text_size(),
363                term.h_to_row(100),
364                term.w_to_col(100)
365            ));
366            term.set_text_size(ts);
367            assert_eq!(term.text_size(), ts);
368            term.append(&format!(
369                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
370                term.text_size(),
371                term.h_to_row(100),
372                term.w_to_col(100)
373            ));
374
375            // Keyboard handler
376            term.handle({
377                move |term, e| {
378                    match e {
379                        fltk::enums::Event::KeyDown
380                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
381                        {
382                            // false to let FLTK handle ESC. true to hide ESC
383                            false
384                        }
385
386                        fltk::enums::Event::KeyDown
387                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
388                        {
389                            // We handle control keystroke
390                            let k = fltk::app::event_text();
391                            term.append_utf8(&k);
392                            true
393                        }
394
395                        fltk::enums::Event::KeyDown
396                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
397                        {
398                            // We handle normal printable keystroke
399                            let k = fltk::app::event_text();
400                            term.take_focus().unwrap();
401                            term.append(&k);
402                            true
403                        }
404
405                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
406                        // We can do this, or else ignore them (return false)
407                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
408                        //     term.redraw();
409                        //     true
410                        // }
411                        _ => false, // Let FLTK handle everything else
412                    }
413                }
414            });
415
416            let attr_save = term.text_attrib();
417            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
418            term.append("\nStartup tests complete. Keyboard is live.\n");
419            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
420            term.set_text_attrib(attr_save);
421            assert_eq!(term.text_attrib(), attr_save);
422            term.redraw();
423        }
424    });
425
426    app.run().unwrap();
427}
Source

pub fn margin_bottom(&self) -> i32

Return the bottom 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!(term.text(true), "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");
115
116            // Testing ansi() and set_ansi() methods
117            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
118            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
119            term.set_ansi(false);
120            assert!(!term.ansi());
121            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
122            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
123            term.append_u8(b"Appending u8 array\n");
124            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
125            term.set_ansi(true); // Restore ANSI state
126
127            // Play with the horizontal scrollbar
128            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
129            term.set_hscrollbar_style(ScrollbarStyle::ON);
130            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
131
132            // Test show_unknown() as incidental part of testing append methods
133            term.set_show_unknown(true);
134            assert!(term.show_unknown());
135            term.append_ascii(
136                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
137            );
138            term.set_show_unknown(false);
139            assert!(!term.show_unknown());
140
141            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
142            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
143
144            let r = term.cursor_row();
145            assert_eq!(term.cursor_col(), 0);
146            term.append(&format!("Testing cursor row/col {r}"));
147            assert_eq!(term.cursor_col(), 24);
148            assert_eq!(term.cursor_row(), r);
149
150            // Test cursor color methods
151            assert_eq!(
152                term.cursor_bg_color(),
153                Color::XtermGreen,
154                "Default cursor bg at startup"
155            );
156            assert_eq!(
157                term.cursor_fg_color(),
158                Color::from_hex(0xff_ff_f0),
159                "Default cursor fg at startup"
160            );
161            term.set_cursor_bg_color(Color::Red);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
164            term.set_cursor_fg_color(Color::Blue);
165            assert_eq!(term.cursor_bg_color(), Color::Red);
166            assert_eq!(term.cursor_fg_color(), Color::Blue);
167            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
168            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
169            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
170            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
171
172            // The default display_rows() will derive from the window size
173            let dr = term.display_rows();
174            let height = term.height();
175            assert_eq!(height, term.h());
176            assert!(dr > 20, "Default display_rows at startup");
177            term.resize(term.x(), term.y(), term.w(), height * 2);
178            assert_eq!(term.h(), height * 2);
179            assert_eq!(height * 2, term.h());
180            assert!(term.display_rows() > dr);
181            term.resize(term.x(), term.y(), term.w(), height);
182
183            // The default display_columns() will derive from the window size
184            let dc = term.display_columns();
185            assert!(dc > 80, "Default display_rows at startup");
186            term.set_display_columns(200);
187            assert_eq!(term.display_columns(), 200);
188            term.append("\n         1         2         3         4         5         6         7         8         9");
189            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
190            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
191            term.set_display_columns(90);
192            assert_eq!(term.display_columns(), 90);
193            term.set_display_columns(dc); // Set back to default
194            assert_eq!(term.display_columns(), dc);
195
196            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
197            term.set_history_rows(50);
198            assert_eq!(term.history_rows(), 50);
199            term.set_history_rows(100); // Set back to default
200            assert_eq!(term.history_rows(), 100);
201
202            let hu = term.history_use();
203            term.append(&format!(
204                "history_use = {hu} (it's not clear what this means)\n"
205            ));
206            // assert_eq!(term.history_use(), hu+1);
207
208            term.append(&format!(
209                "margins = b:{} l:{} r:{} t{}\n",
210                term.margin_bottom(),
211                term.margin_left(),
212                term.margin_right(),
213                term.margin_top()
214            ));
215            assert_eq!(term.margin_bottom(), 3);
216            assert_eq!(term.margin_left(), 3);
217            assert_eq!(term.margin_right(), 3);
218            assert_eq!(term.margin_top(), 3);
219
220            term.set_margin_bottom(5);
221            term.set_margin_left(10);
222            term.set_margin_right(15);
223            term.set_margin_top(20);
224            assert_eq!(term.margin_bottom(), 5);
225            assert_eq!(term.margin_left(), 10);
226            assert_eq!(term.margin_right(), 15);
227            assert_eq!(term.margin_top(), 20);
228
229            term.append("Single character: '");
230            term.print_char('X');
231            term.append("', single UTF-8 character: '");
232            term.print_char_utf8('↑');
233            term.append("'\n");
234
235            let rr = term.redraw_rate();
236            assert_eq!(rr, 0.1, "Default redraw rate at startup");
237            term.append(&format!("Redraw rate {rr}\n"));
238            term.set_redraw_rate(1.0);
239            assert_eq!(term.redraw_rate(), 1.0);
240            term.set_redraw_rate(rr);
241            assert_eq!(term.redraw_rate(), rr);
242
243            let rs = term.redraw_style();
244            term.append(&format!("Redraw style {rs:?}\n"));
245            assert_eq!(
246                rs,
247                RedrawStyle::RateLimited,
248                "Default redraw style at startup"
249            );
250            term.set_redraw_style(RedrawStyle::NoRedraw);
251            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
252            term.set_redraw_style(rs);
253            assert_eq!(term.redraw_style(), rs);
254
255            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
256            assert_eq!(
257                RedrawStyle::NoRedraw.bits(),
258                0x0000,
259                "RedrawStyle enum values have been reassigned"
260            );
261            assert_eq!(
262                RedrawStyle::RateLimited.bits(),
263                0x0001,
264                "RedrawStyle enum values have been reassigned"
265            );
266            assert_eq!(
267                RedrawStyle::PerWrite.bits(),
268                0x0002,
269                "RedrawStyle enum values have been reassigned"
270            );
271
272            let sb = term.scrollbar();
273            let hsb = term.hscrollbar();
274            // Both vertical and horizontal scrollbars are at zero
275            assert_eq!(sb.value(), 0.0);
276            assert_eq!(hsb.value(), 0.0);
277            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
278
279            term.append(&format!(
280                "Scrollbar actual size {}\n",
281                term.scrollbar_actual_size()
282            ));
283            assert_eq!(term.scrollbar_actual_size(), 16);
284            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
285            assert_eq!(
286                term.scrollbar_size(),
287                0,
288                "Default scrollbar size at startup"
289            );
290            term.set_scrollbar_size(40);
291            assert_eq!(term.scrollbar_size(), 40);
292            assert_eq!(term.scrollbar_actual_size(), 40);
293            term.append(&format!(
294                "Scrollbar actual size {}\n",
295                term.scrollbar_actual_size()
296            ));
297            term.set_scrollbar_size(0); // Restore default
298            assert_eq!(term.scrollbar_size(), 0);
299            assert_eq!(term.scrollbar_actual_size(), 16);
300
301            let sfc = term.selection_fg_color();
302            let sbc = term.selection_bg_color();
303            assert_eq!(sfc, Color::Black);
304            assert_eq!(sbc, Color::White);
305            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
306            term.set_selection_fg_color(Color::Green);
307            term.set_selection_bg_color(Color::DarkBlue);
308            assert_eq!(term.selection_fg_color(), Color::Green);
309            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
310            term.set_selection_fg_color(sfc);
311            term.set_selection_bg_color(sbc);
312            assert_eq!(term.selection_fg_color(), Color::Black);
313            assert_eq!(term.selection_bg_color(), Color::White);
314
315            let tfcd = term.text_fg_color_default();
316            let tbcd = term.text_bg_color_default();
317            assert_eq!(tfcd, Color::XtermWhite);
318            assert_eq!(tbcd, Color::TransparentBg);
319            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
320            term.set_text_fg_color_default(Color::Green);
321            term.set_text_bg_color_default(Color::DarkBlue);
322            assert_eq!(term.text_fg_color_default(), Color::Green);
323            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
324            term.set_text_fg_color_default(tfcd);
325            term.set_text_bg_color_default(tbcd);
326            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
327            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
328
329            let tfc = term.text_fg_color();
330            let tbc = term.text_bg_color();
331            assert_eq!(tfc, Color::XtermWhite);
332            assert_eq!(tbc, Color::TransparentBg);
333            term.append(&format!("Text colors: {sfc} {sbc}\n"));
334            term.set_text_fg_color(Color::Green);
335            term.set_text_bg_color(Color::DarkBlue);
336            assert_eq!(term.text_fg_color(), Color::Green);
337            assert_eq!(term.text_bg_color(), Color::DarkBlue);
338            term.set_text_fg_color(tfc);
339            term.set_text_bg_color(tbc);
340            assert_eq!(term.text_fg_color(), Color::XtermWhite);
341            assert_eq!(term.text_bg_color(), Color::TransparentBg);
342
343            let tf = term.text_font();
344            term.append(&format!("Text font: {tf:?}\n"));
345            assert_eq!(tf, Font::Courier);
346            term.set_text_font(Font::Screen);
347            assert_eq!(term.text_font(), Font::Screen);
348            term.set_text_font(tf);
349            assert_eq!(term.text_font(), Font::Courier);
350
351            let ts = term.text_size();
352            let r = term.h_to_row(100);
353            let c = term.w_to_col(100);
354            term.append(&format!(
355                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
356            ));
357            assert_eq!(ts, 14);
358            term.set_text_size(30);
359            assert_eq!(term.text_size(), 30);
360            term.append(&format!(
361                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
362                term.text_size(),
363                term.h_to_row(100),
364                term.w_to_col(100)
365            ));
366            term.set_text_size(ts);
367            assert_eq!(term.text_size(), ts);
368            term.append(&format!(
369                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
370                term.text_size(),
371                term.h_to_row(100),
372                term.w_to_col(100)
373            ));
374
375            // Keyboard handler
376            term.handle({
377                move |term, e| {
378                    match e {
379                        fltk::enums::Event::KeyDown
380                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
381                        {
382                            // false to let FLTK handle ESC. true to hide ESC
383                            false
384                        }
385
386                        fltk::enums::Event::KeyDown
387                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
388                        {
389                            // We handle control keystroke
390                            let k = fltk::app::event_text();
391                            term.append_utf8(&k);
392                            true
393                        }
394
395                        fltk::enums::Event::KeyDown
396                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
397                        {
398                            // We handle normal printable keystroke
399                            let k = fltk::app::event_text();
400                            term.take_focus().unwrap();
401                            term.append(&k);
402                            true
403                        }
404
405                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
406                        // We can do this, or else ignore them (return false)
407                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
408                        //     term.redraw();
409                        //     true
410                        // }
411                        _ => false, // Let FLTK handle everything else
412                    }
413                }
414            });
415
416            let attr_save = term.text_attrib();
417            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
418            term.append("\nStartup tests complete. Keyboard is live.\n");
419            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
420            term.set_text_attrib(attr_save);
421            assert_eq!(term.text_attrib(), attr_save);
422            term.redraw();
423        }
424    });
425
426    app.run().unwrap();
427}
Source

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

Set the left margin

Examples found in repository?
examples/terminal.rs (line 221)
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!(term.text(true), "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");
115
116            // Testing ansi() and set_ansi() methods
117            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
118            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
119            term.set_ansi(false);
120            assert!(!term.ansi());
121            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
122            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
123            term.append_u8(b"Appending u8 array\n");
124            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
125            term.set_ansi(true); // Restore ANSI state
126
127            // Play with the horizontal scrollbar
128            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
129            term.set_hscrollbar_style(ScrollbarStyle::ON);
130            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
131
132            // Test show_unknown() as incidental part of testing append methods
133            term.set_show_unknown(true);
134            assert!(term.show_unknown());
135            term.append_ascii(
136                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
137            );
138            term.set_show_unknown(false);
139            assert!(!term.show_unknown());
140
141            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
142            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
143
144            let r = term.cursor_row();
145            assert_eq!(term.cursor_col(), 0);
146            term.append(&format!("Testing cursor row/col {r}"));
147            assert_eq!(term.cursor_col(), 24);
148            assert_eq!(term.cursor_row(), r);
149
150            // Test cursor color methods
151            assert_eq!(
152                term.cursor_bg_color(),
153                Color::XtermGreen,
154                "Default cursor bg at startup"
155            );
156            assert_eq!(
157                term.cursor_fg_color(),
158                Color::from_hex(0xff_ff_f0),
159                "Default cursor fg at startup"
160            );
161            term.set_cursor_bg_color(Color::Red);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
164            term.set_cursor_fg_color(Color::Blue);
165            assert_eq!(term.cursor_bg_color(), Color::Red);
166            assert_eq!(term.cursor_fg_color(), Color::Blue);
167            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
168            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
169            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
170            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
171
172            // The default display_rows() will derive from the window size
173            let dr = term.display_rows();
174            let height = term.height();
175            assert_eq!(height, term.h());
176            assert!(dr > 20, "Default display_rows at startup");
177            term.resize(term.x(), term.y(), term.w(), height * 2);
178            assert_eq!(term.h(), height * 2);
179            assert_eq!(height * 2, term.h());
180            assert!(term.display_rows() > dr);
181            term.resize(term.x(), term.y(), term.w(), height);
182
183            // The default display_columns() will derive from the window size
184            let dc = term.display_columns();
185            assert!(dc > 80, "Default display_rows at startup");
186            term.set_display_columns(200);
187            assert_eq!(term.display_columns(), 200);
188            term.append("\n         1         2         3         4         5         6         7         8         9");
189            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
190            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
191            term.set_display_columns(90);
192            assert_eq!(term.display_columns(), 90);
193            term.set_display_columns(dc); // Set back to default
194            assert_eq!(term.display_columns(), dc);
195
196            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
197            term.set_history_rows(50);
198            assert_eq!(term.history_rows(), 50);
199            term.set_history_rows(100); // Set back to default
200            assert_eq!(term.history_rows(), 100);
201
202            let hu = term.history_use();
203            term.append(&format!(
204                "history_use = {hu} (it's not clear what this means)\n"
205            ));
206            // assert_eq!(term.history_use(), hu+1);
207
208            term.append(&format!(
209                "margins = b:{} l:{} r:{} t{}\n",
210                term.margin_bottom(),
211                term.margin_left(),
212                term.margin_right(),
213                term.margin_top()
214            ));
215            assert_eq!(term.margin_bottom(), 3);
216            assert_eq!(term.margin_left(), 3);
217            assert_eq!(term.margin_right(), 3);
218            assert_eq!(term.margin_top(), 3);
219
220            term.set_margin_bottom(5);
221            term.set_margin_left(10);
222            term.set_margin_right(15);
223            term.set_margin_top(20);
224            assert_eq!(term.margin_bottom(), 5);
225            assert_eq!(term.margin_left(), 10);
226            assert_eq!(term.margin_right(), 15);
227            assert_eq!(term.margin_top(), 20);
228
229            term.append("Single character: '");
230            term.print_char('X');
231            term.append("', single UTF-8 character: '");
232            term.print_char_utf8('↑');
233            term.append("'\n");
234
235            let rr = term.redraw_rate();
236            assert_eq!(rr, 0.1, "Default redraw rate at startup");
237            term.append(&format!("Redraw rate {rr}\n"));
238            term.set_redraw_rate(1.0);
239            assert_eq!(term.redraw_rate(), 1.0);
240            term.set_redraw_rate(rr);
241            assert_eq!(term.redraw_rate(), rr);
242
243            let rs = term.redraw_style();
244            term.append(&format!("Redraw style {rs:?}\n"));
245            assert_eq!(
246                rs,
247                RedrawStyle::RateLimited,
248                "Default redraw style at startup"
249            );
250            term.set_redraw_style(RedrawStyle::NoRedraw);
251            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
252            term.set_redraw_style(rs);
253            assert_eq!(term.redraw_style(), rs);
254
255            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
256            assert_eq!(
257                RedrawStyle::NoRedraw.bits(),
258                0x0000,
259                "RedrawStyle enum values have been reassigned"
260            );
261            assert_eq!(
262                RedrawStyle::RateLimited.bits(),
263                0x0001,
264                "RedrawStyle enum values have been reassigned"
265            );
266            assert_eq!(
267                RedrawStyle::PerWrite.bits(),
268                0x0002,
269                "RedrawStyle enum values have been reassigned"
270            );
271
272            let sb = term.scrollbar();
273            let hsb = term.hscrollbar();
274            // Both vertical and horizontal scrollbars are at zero
275            assert_eq!(sb.value(), 0.0);
276            assert_eq!(hsb.value(), 0.0);
277            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
278
279            term.append(&format!(
280                "Scrollbar actual size {}\n",
281                term.scrollbar_actual_size()
282            ));
283            assert_eq!(term.scrollbar_actual_size(), 16);
284            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
285            assert_eq!(
286                term.scrollbar_size(),
287                0,
288                "Default scrollbar size at startup"
289            );
290            term.set_scrollbar_size(40);
291            assert_eq!(term.scrollbar_size(), 40);
292            assert_eq!(term.scrollbar_actual_size(), 40);
293            term.append(&format!(
294                "Scrollbar actual size {}\n",
295                term.scrollbar_actual_size()
296            ));
297            term.set_scrollbar_size(0); // Restore default
298            assert_eq!(term.scrollbar_size(), 0);
299            assert_eq!(term.scrollbar_actual_size(), 16);
300
301            let sfc = term.selection_fg_color();
302            let sbc = term.selection_bg_color();
303            assert_eq!(sfc, Color::Black);
304            assert_eq!(sbc, Color::White);
305            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
306            term.set_selection_fg_color(Color::Green);
307            term.set_selection_bg_color(Color::DarkBlue);
308            assert_eq!(term.selection_fg_color(), Color::Green);
309            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
310            term.set_selection_fg_color(sfc);
311            term.set_selection_bg_color(sbc);
312            assert_eq!(term.selection_fg_color(), Color::Black);
313            assert_eq!(term.selection_bg_color(), Color::White);
314
315            let tfcd = term.text_fg_color_default();
316            let tbcd = term.text_bg_color_default();
317            assert_eq!(tfcd, Color::XtermWhite);
318            assert_eq!(tbcd, Color::TransparentBg);
319            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
320            term.set_text_fg_color_default(Color::Green);
321            term.set_text_bg_color_default(Color::DarkBlue);
322            assert_eq!(term.text_fg_color_default(), Color::Green);
323            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
324            term.set_text_fg_color_default(tfcd);
325            term.set_text_bg_color_default(tbcd);
326            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
327            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
328
329            let tfc = term.text_fg_color();
330            let tbc = term.text_bg_color();
331            assert_eq!(tfc, Color::XtermWhite);
332            assert_eq!(tbc, Color::TransparentBg);
333            term.append(&format!("Text colors: {sfc} {sbc}\n"));
334            term.set_text_fg_color(Color::Green);
335            term.set_text_bg_color(Color::DarkBlue);
336            assert_eq!(term.text_fg_color(), Color::Green);
337            assert_eq!(term.text_bg_color(), Color::DarkBlue);
338            term.set_text_fg_color(tfc);
339            term.set_text_bg_color(tbc);
340            assert_eq!(term.text_fg_color(), Color::XtermWhite);
341            assert_eq!(term.text_bg_color(), Color::TransparentBg);
342
343            let tf = term.text_font();
344            term.append(&format!("Text font: {tf:?}\n"));
345            assert_eq!(tf, Font::Courier);
346            term.set_text_font(Font::Screen);
347            assert_eq!(term.text_font(), Font::Screen);
348            term.set_text_font(tf);
349            assert_eq!(term.text_font(), Font::Courier);
350
351            let ts = term.text_size();
352            let r = term.h_to_row(100);
353            let c = term.w_to_col(100);
354            term.append(&format!(
355                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
356            ));
357            assert_eq!(ts, 14);
358            term.set_text_size(30);
359            assert_eq!(term.text_size(), 30);
360            term.append(&format!(
361                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
362                term.text_size(),
363                term.h_to_row(100),
364                term.w_to_col(100)
365            ));
366            term.set_text_size(ts);
367            assert_eq!(term.text_size(), ts);
368            term.append(&format!(
369                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
370                term.text_size(),
371                term.h_to_row(100),
372                term.w_to_col(100)
373            ));
374
375            // Keyboard handler
376            term.handle({
377                move |term, e| {
378                    match e {
379                        fltk::enums::Event::KeyDown
380                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
381                        {
382                            // false to let FLTK handle ESC. true to hide ESC
383                            false
384                        }
385
386                        fltk::enums::Event::KeyDown
387                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
388                        {
389                            // We handle control keystroke
390                            let k = fltk::app::event_text();
391                            term.append_utf8(&k);
392                            true
393                        }
394
395                        fltk::enums::Event::KeyDown
396                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
397                        {
398                            // We handle normal printable keystroke
399                            let k = fltk::app::event_text();
400                            term.take_focus().unwrap();
401                            term.append(&k);
402                            true
403                        }
404
405                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
406                        // We can do this, or else ignore them (return false)
407                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
408                        //     term.redraw();
409                        //     true
410                        // }
411                        _ => false, // Let FLTK handle everything else
412                    }
413                }
414            });
415
416            let attr_save = term.text_attrib();
417            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
418            term.append("\nStartup tests complete. Keyboard is live.\n");
419            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
420            term.set_text_attrib(attr_save);
421            assert_eq!(term.text_attrib(), attr_save);
422            term.redraw();
423        }
424    });
425
426    app.run().unwrap();
427}
Source

pub fn margin_left(&self) -> i32

Return the left margin

Examples found in repository?
examples/terminal.rs (line 211)
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!(term.text(true), "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");
115
116            // Testing ansi() and set_ansi() methods
117            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
118            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
119            term.set_ansi(false);
120            assert!(!term.ansi());
121            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
122            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
123            term.append_u8(b"Appending u8 array\n");
124            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
125            term.set_ansi(true); // Restore ANSI state
126
127            // Play with the horizontal scrollbar
128            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
129            term.set_hscrollbar_style(ScrollbarStyle::ON);
130            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
131
132            // Test show_unknown() as incidental part of testing append methods
133            term.set_show_unknown(true);
134            assert!(term.show_unknown());
135            term.append_ascii(
136                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
137            );
138            term.set_show_unknown(false);
139            assert!(!term.show_unknown());
140
141            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
142            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
143
144            let r = term.cursor_row();
145            assert_eq!(term.cursor_col(), 0);
146            term.append(&format!("Testing cursor row/col {r}"));
147            assert_eq!(term.cursor_col(), 24);
148            assert_eq!(term.cursor_row(), r);
149
150            // Test cursor color methods
151            assert_eq!(
152                term.cursor_bg_color(),
153                Color::XtermGreen,
154                "Default cursor bg at startup"
155            );
156            assert_eq!(
157                term.cursor_fg_color(),
158                Color::from_hex(0xff_ff_f0),
159                "Default cursor fg at startup"
160            );
161            term.set_cursor_bg_color(Color::Red);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
164            term.set_cursor_fg_color(Color::Blue);
165            assert_eq!(term.cursor_bg_color(), Color::Red);
166            assert_eq!(term.cursor_fg_color(), Color::Blue);
167            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
168            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
169            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
170            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
171
172            // The default display_rows() will derive from the window size
173            let dr = term.display_rows();
174            let height = term.height();
175            assert_eq!(height, term.h());
176            assert!(dr > 20, "Default display_rows at startup");
177            term.resize(term.x(), term.y(), term.w(), height * 2);
178            assert_eq!(term.h(), height * 2);
179            assert_eq!(height * 2, term.h());
180            assert!(term.display_rows() > dr);
181            term.resize(term.x(), term.y(), term.w(), height);
182
183            // The default display_columns() will derive from the window size
184            let dc = term.display_columns();
185            assert!(dc > 80, "Default display_rows at startup");
186            term.set_display_columns(200);
187            assert_eq!(term.display_columns(), 200);
188            term.append("\n         1         2         3         4         5         6         7         8         9");
189            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
190            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
191            term.set_display_columns(90);
192            assert_eq!(term.display_columns(), 90);
193            term.set_display_columns(dc); // Set back to default
194            assert_eq!(term.display_columns(), dc);
195
196            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
197            term.set_history_rows(50);
198            assert_eq!(term.history_rows(), 50);
199            term.set_history_rows(100); // Set back to default
200            assert_eq!(term.history_rows(), 100);
201
202            let hu = term.history_use();
203            term.append(&format!(
204                "history_use = {hu} (it's not clear what this means)\n"
205            ));
206            // assert_eq!(term.history_use(), hu+1);
207
208            term.append(&format!(
209                "margins = b:{} l:{} r:{} t{}\n",
210                term.margin_bottom(),
211                term.margin_left(),
212                term.margin_right(),
213                term.margin_top()
214            ));
215            assert_eq!(term.margin_bottom(), 3);
216            assert_eq!(term.margin_left(), 3);
217            assert_eq!(term.margin_right(), 3);
218            assert_eq!(term.margin_top(), 3);
219
220            term.set_margin_bottom(5);
221            term.set_margin_left(10);
222            term.set_margin_right(15);
223            term.set_margin_top(20);
224            assert_eq!(term.margin_bottom(), 5);
225            assert_eq!(term.margin_left(), 10);
226            assert_eq!(term.margin_right(), 15);
227            assert_eq!(term.margin_top(), 20);
228
229            term.append("Single character: '");
230            term.print_char('X');
231            term.append("', single UTF-8 character: '");
232            term.print_char_utf8('↑');
233            term.append("'\n");
234
235            let rr = term.redraw_rate();
236            assert_eq!(rr, 0.1, "Default redraw rate at startup");
237            term.append(&format!("Redraw rate {rr}\n"));
238            term.set_redraw_rate(1.0);
239            assert_eq!(term.redraw_rate(), 1.0);
240            term.set_redraw_rate(rr);
241            assert_eq!(term.redraw_rate(), rr);
242
243            let rs = term.redraw_style();
244            term.append(&format!("Redraw style {rs:?}\n"));
245            assert_eq!(
246                rs,
247                RedrawStyle::RateLimited,
248                "Default redraw style at startup"
249            );
250            term.set_redraw_style(RedrawStyle::NoRedraw);
251            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
252            term.set_redraw_style(rs);
253            assert_eq!(term.redraw_style(), rs);
254
255            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
256            assert_eq!(
257                RedrawStyle::NoRedraw.bits(),
258                0x0000,
259                "RedrawStyle enum values have been reassigned"
260            );
261            assert_eq!(
262                RedrawStyle::RateLimited.bits(),
263                0x0001,
264                "RedrawStyle enum values have been reassigned"
265            );
266            assert_eq!(
267                RedrawStyle::PerWrite.bits(),
268                0x0002,
269                "RedrawStyle enum values have been reassigned"
270            );
271
272            let sb = term.scrollbar();
273            let hsb = term.hscrollbar();
274            // Both vertical and horizontal scrollbars are at zero
275            assert_eq!(sb.value(), 0.0);
276            assert_eq!(hsb.value(), 0.0);
277            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
278
279            term.append(&format!(
280                "Scrollbar actual size {}\n",
281                term.scrollbar_actual_size()
282            ));
283            assert_eq!(term.scrollbar_actual_size(), 16);
284            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
285            assert_eq!(
286                term.scrollbar_size(),
287                0,
288                "Default scrollbar size at startup"
289            );
290            term.set_scrollbar_size(40);
291            assert_eq!(term.scrollbar_size(), 40);
292            assert_eq!(term.scrollbar_actual_size(), 40);
293            term.append(&format!(
294                "Scrollbar actual size {}\n",
295                term.scrollbar_actual_size()
296            ));
297            term.set_scrollbar_size(0); // Restore default
298            assert_eq!(term.scrollbar_size(), 0);
299            assert_eq!(term.scrollbar_actual_size(), 16);
300
301            let sfc = term.selection_fg_color();
302            let sbc = term.selection_bg_color();
303            assert_eq!(sfc, Color::Black);
304            assert_eq!(sbc, Color::White);
305            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
306            term.set_selection_fg_color(Color::Green);
307            term.set_selection_bg_color(Color::DarkBlue);
308            assert_eq!(term.selection_fg_color(), Color::Green);
309            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
310            term.set_selection_fg_color(sfc);
311            term.set_selection_bg_color(sbc);
312            assert_eq!(term.selection_fg_color(), Color::Black);
313            assert_eq!(term.selection_bg_color(), Color::White);
314
315            let tfcd = term.text_fg_color_default();
316            let tbcd = term.text_bg_color_default();
317            assert_eq!(tfcd, Color::XtermWhite);
318            assert_eq!(tbcd, Color::TransparentBg);
319            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
320            term.set_text_fg_color_default(Color::Green);
321            term.set_text_bg_color_default(Color::DarkBlue);
322            assert_eq!(term.text_fg_color_default(), Color::Green);
323            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
324            term.set_text_fg_color_default(tfcd);
325            term.set_text_bg_color_default(tbcd);
326            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
327            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
328
329            let tfc = term.text_fg_color();
330            let tbc = term.text_bg_color();
331            assert_eq!(tfc, Color::XtermWhite);
332            assert_eq!(tbc, Color::TransparentBg);
333            term.append(&format!("Text colors: {sfc} {sbc}\n"));
334            term.set_text_fg_color(Color::Green);
335            term.set_text_bg_color(Color::DarkBlue);
336            assert_eq!(term.text_fg_color(), Color::Green);
337            assert_eq!(term.text_bg_color(), Color::DarkBlue);
338            term.set_text_fg_color(tfc);
339            term.set_text_bg_color(tbc);
340            assert_eq!(term.text_fg_color(), Color::XtermWhite);
341            assert_eq!(term.text_bg_color(), Color::TransparentBg);
342
343            let tf = term.text_font();
344            term.append(&format!("Text font: {tf:?}\n"));
345            assert_eq!(tf, Font::Courier);
346            term.set_text_font(Font::Screen);
347            assert_eq!(term.text_font(), Font::Screen);
348            term.set_text_font(tf);
349            assert_eq!(term.text_font(), Font::Courier);
350
351            let ts = term.text_size();
352            let r = term.h_to_row(100);
353            let c = term.w_to_col(100);
354            term.append(&format!(
355                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
356            ));
357            assert_eq!(ts, 14);
358            term.set_text_size(30);
359            assert_eq!(term.text_size(), 30);
360            term.append(&format!(
361                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
362                term.text_size(),
363                term.h_to_row(100),
364                term.w_to_col(100)
365            ));
366            term.set_text_size(ts);
367            assert_eq!(term.text_size(), ts);
368            term.append(&format!(
369                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
370                term.text_size(),
371                term.h_to_row(100),
372                term.w_to_col(100)
373            ));
374
375            // Keyboard handler
376            term.handle({
377                move |term, e| {
378                    match e {
379                        fltk::enums::Event::KeyDown
380                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
381                        {
382                            // false to let FLTK handle ESC. true to hide ESC
383                            false
384                        }
385
386                        fltk::enums::Event::KeyDown
387                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
388                        {
389                            // We handle control keystroke
390                            let k = fltk::app::event_text();
391                            term.append_utf8(&k);
392                            true
393                        }
394
395                        fltk::enums::Event::KeyDown
396                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
397                        {
398                            // We handle normal printable keystroke
399                            let k = fltk::app::event_text();
400                            term.take_focus().unwrap();
401                            term.append(&k);
402                            true
403                        }
404
405                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
406                        // We can do this, or else ignore them (return false)
407                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
408                        //     term.redraw();
409                        //     true
410                        // }
411                        _ => false, // Let FLTK handle everything else
412                    }
413                }
414            });
415
416            let attr_save = term.text_attrib();
417            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
418            term.append("\nStartup tests complete. Keyboard is live.\n");
419            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
420            term.set_text_attrib(attr_save);
421            assert_eq!(term.text_attrib(), attr_save);
422            term.redraw();
423        }
424    });
425
426    app.run().unwrap();
427}
Source

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

Set the right margin

Examples found in repository?
examples/terminal.rs (line 222)
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!(term.text(true), "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");
115
116            // Testing ansi() and set_ansi() methods
117            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
118            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
119            term.set_ansi(false);
120            assert!(!term.ansi());
121            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
122            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
123            term.append_u8(b"Appending u8 array\n");
124            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
125            term.set_ansi(true); // Restore ANSI state
126
127            // Play with the horizontal scrollbar
128            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
129            term.set_hscrollbar_style(ScrollbarStyle::ON);
130            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
131
132            // Test show_unknown() as incidental part of testing append methods
133            term.set_show_unknown(true);
134            assert!(term.show_unknown());
135            term.append_ascii(
136                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
137            );
138            term.set_show_unknown(false);
139            assert!(!term.show_unknown());
140
141            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
142            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
143
144            let r = term.cursor_row();
145            assert_eq!(term.cursor_col(), 0);
146            term.append(&format!("Testing cursor row/col {r}"));
147            assert_eq!(term.cursor_col(), 24);
148            assert_eq!(term.cursor_row(), r);
149
150            // Test cursor color methods
151            assert_eq!(
152                term.cursor_bg_color(),
153                Color::XtermGreen,
154                "Default cursor bg at startup"
155            );
156            assert_eq!(
157                term.cursor_fg_color(),
158                Color::from_hex(0xff_ff_f0),
159                "Default cursor fg at startup"
160            );
161            term.set_cursor_bg_color(Color::Red);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
164            term.set_cursor_fg_color(Color::Blue);
165            assert_eq!(term.cursor_bg_color(), Color::Red);
166            assert_eq!(term.cursor_fg_color(), Color::Blue);
167            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
168            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
169            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
170            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
171
172            // The default display_rows() will derive from the window size
173            let dr = term.display_rows();
174            let height = term.height();
175            assert_eq!(height, term.h());
176            assert!(dr > 20, "Default display_rows at startup");
177            term.resize(term.x(), term.y(), term.w(), height * 2);
178            assert_eq!(term.h(), height * 2);
179            assert_eq!(height * 2, term.h());
180            assert!(term.display_rows() > dr);
181            term.resize(term.x(), term.y(), term.w(), height);
182
183            // The default display_columns() will derive from the window size
184            let dc = term.display_columns();
185            assert!(dc > 80, "Default display_rows at startup");
186            term.set_display_columns(200);
187            assert_eq!(term.display_columns(), 200);
188            term.append("\n         1         2         3         4         5         6         7         8         9");
189            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
190            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
191            term.set_display_columns(90);
192            assert_eq!(term.display_columns(), 90);
193            term.set_display_columns(dc); // Set back to default
194            assert_eq!(term.display_columns(), dc);
195
196            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
197            term.set_history_rows(50);
198            assert_eq!(term.history_rows(), 50);
199            term.set_history_rows(100); // Set back to default
200            assert_eq!(term.history_rows(), 100);
201
202            let hu = term.history_use();
203            term.append(&format!(
204                "history_use = {hu} (it's not clear what this means)\n"
205            ));
206            // assert_eq!(term.history_use(), hu+1);
207
208            term.append(&format!(
209                "margins = b:{} l:{} r:{} t{}\n",
210                term.margin_bottom(),
211                term.margin_left(),
212                term.margin_right(),
213                term.margin_top()
214            ));
215            assert_eq!(term.margin_bottom(), 3);
216            assert_eq!(term.margin_left(), 3);
217            assert_eq!(term.margin_right(), 3);
218            assert_eq!(term.margin_top(), 3);
219
220            term.set_margin_bottom(5);
221            term.set_margin_left(10);
222            term.set_margin_right(15);
223            term.set_margin_top(20);
224            assert_eq!(term.margin_bottom(), 5);
225            assert_eq!(term.margin_left(), 10);
226            assert_eq!(term.margin_right(), 15);
227            assert_eq!(term.margin_top(), 20);
228
229            term.append("Single character: '");
230            term.print_char('X');
231            term.append("', single UTF-8 character: '");
232            term.print_char_utf8('↑');
233            term.append("'\n");
234
235            let rr = term.redraw_rate();
236            assert_eq!(rr, 0.1, "Default redraw rate at startup");
237            term.append(&format!("Redraw rate {rr}\n"));
238            term.set_redraw_rate(1.0);
239            assert_eq!(term.redraw_rate(), 1.0);
240            term.set_redraw_rate(rr);
241            assert_eq!(term.redraw_rate(), rr);
242
243            let rs = term.redraw_style();
244            term.append(&format!("Redraw style {rs:?}\n"));
245            assert_eq!(
246                rs,
247                RedrawStyle::RateLimited,
248                "Default redraw style at startup"
249            );
250            term.set_redraw_style(RedrawStyle::NoRedraw);
251            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
252            term.set_redraw_style(rs);
253            assert_eq!(term.redraw_style(), rs);
254
255            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
256            assert_eq!(
257                RedrawStyle::NoRedraw.bits(),
258                0x0000,
259                "RedrawStyle enum values have been reassigned"
260            );
261            assert_eq!(
262                RedrawStyle::RateLimited.bits(),
263                0x0001,
264                "RedrawStyle enum values have been reassigned"
265            );
266            assert_eq!(
267                RedrawStyle::PerWrite.bits(),
268                0x0002,
269                "RedrawStyle enum values have been reassigned"
270            );
271
272            let sb = term.scrollbar();
273            let hsb = term.hscrollbar();
274            // Both vertical and horizontal scrollbars are at zero
275            assert_eq!(sb.value(), 0.0);
276            assert_eq!(hsb.value(), 0.0);
277            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
278
279            term.append(&format!(
280                "Scrollbar actual size {}\n",
281                term.scrollbar_actual_size()
282            ));
283            assert_eq!(term.scrollbar_actual_size(), 16);
284            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
285            assert_eq!(
286                term.scrollbar_size(),
287                0,
288                "Default scrollbar size at startup"
289            );
290            term.set_scrollbar_size(40);
291            assert_eq!(term.scrollbar_size(), 40);
292            assert_eq!(term.scrollbar_actual_size(), 40);
293            term.append(&format!(
294                "Scrollbar actual size {}\n",
295                term.scrollbar_actual_size()
296            ));
297            term.set_scrollbar_size(0); // Restore default
298            assert_eq!(term.scrollbar_size(), 0);
299            assert_eq!(term.scrollbar_actual_size(), 16);
300
301            let sfc = term.selection_fg_color();
302            let sbc = term.selection_bg_color();
303            assert_eq!(sfc, Color::Black);
304            assert_eq!(sbc, Color::White);
305            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
306            term.set_selection_fg_color(Color::Green);
307            term.set_selection_bg_color(Color::DarkBlue);
308            assert_eq!(term.selection_fg_color(), Color::Green);
309            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
310            term.set_selection_fg_color(sfc);
311            term.set_selection_bg_color(sbc);
312            assert_eq!(term.selection_fg_color(), Color::Black);
313            assert_eq!(term.selection_bg_color(), Color::White);
314
315            let tfcd = term.text_fg_color_default();
316            let tbcd = term.text_bg_color_default();
317            assert_eq!(tfcd, Color::XtermWhite);
318            assert_eq!(tbcd, Color::TransparentBg);
319            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
320            term.set_text_fg_color_default(Color::Green);
321            term.set_text_bg_color_default(Color::DarkBlue);
322            assert_eq!(term.text_fg_color_default(), Color::Green);
323            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
324            term.set_text_fg_color_default(tfcd);
325            term.set_text_bg_color_default(tbcd);
326            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
327            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
328
329            let tfc = term.text_fg_color();
330            let tbc = term.text_bg_color();
331            assert_eq!(tfc, Color::XtermWhite);
332            assert_eq!(tbc, Color::TransparentBg);
333            term.append(&format!("Text colors: {sfc} {sbc}\n"));
334            term.set_text_fg_color(Color::Green);
335            term.set_text_bg_color(Color::DarkBlue);
336            assert_eq!(term.text_fg_color(), Color::Green);
337            assert_eq!(term.text_bg_color(), Color::DarkBlue);
338            term.set_text_fg_color(tfc);
339            term.set_text_bg_color(tbc);
340            assert_eq!(term.text_fg_color(), Color::XtermWhite);
341            assert_eq!(term.text_bg_color(), Color::TransparentBg);
342
343            let tf = term.text_font();
344            term.append(&format!("Text font: {tf:?}\n"));
345            assert_eq!(tf, Font::Courier);
346            term.set_text_font(Font::Screen);
347            assert_eq!(term.text_font(), Font::Screen);
348            term.set_text_font(tf);
349            assert_eq!(term.text_font(), Font::Courier);
350
351            let ts = term.text_size();
352            let r = term.h_to_row(100);
353            let c = term.w_to_col(100);
354            term.append(&format!(
355                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
356            ));
357            assert_eq!(ts, 14);
358            term.set_text_size(30);
359            assert_eq!(term.text_size(), 30);
360            term.append(&format!(
361                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
362                term.text_size(),
363                term.h_to_row(100),
364                term.w_to_col(100)
365            ));
366            term.set_text_size(ts);
367            assert_eq!(term.text_size(), ts);
368            term.append(&format!(
369                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
370                term.text_size(),
371                term.h_to_row(100),
372                term.w_to_col(100)
373            ));
374
375            // Keyboard handler
376            term.handle({
377                move |term, e| {
378                    match e {
379                        fltk::enums::Event::KeyDown
380                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
381                        {
382                            // false to let FLTK handle ESC. true to hide ESC
383                            false
384                        }
385
386                        fltk::enums::Event::KeyDown
387                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
388                        {
389                            // We handle control keystroke
390                            let k = fltk::app::event_text();
391                            term.append_utf8(&k);
392                            true
393                        }
394
395                        fltk::enums::Event::KeyDown
396                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
397                        {
398                            // We handle normal printable keystroke
399                            let k = fltk::app::event_text();
400                            term.take_focus().unwrap();
401                            term.append(&k);
402                            true
403                        }
404
405                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
406                        // We can do this, or else ignore them (return false)
407                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
408                        //     term.redraw();
409                        //     true
410                        // }
411                        _ => false, // Let FLTK handle everything else
412                    }
413                }
414            });
415
416            let attr_save = term.text_attrib();
417            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
418            term.append("\nStartup tests complete. Keyboard is live.\n");
419            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
420            term.set_text_attrib(attr_save);
421            assert_eq!(term.text_attrib(), attr_save);
422            term.redraw();
423        }
424    });
425
426    app.run().unwrap();
427}
Source

pub fn margin_right(&self) -> i32

Return the right margin

Examples found in repository?
examples/terminal.rs (line 212)
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!(term.text(true), "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");
115
116            // Testing ansi() and set_ansi() methods
117            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
118            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
119            term.set_ansi(false);
120            assert!(!term.ansi());
121            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
122            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
123            term.append_u8(b"Appending u8 array\n");
124            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
125            term.set_ansi(true); // Restore ANSI state
126
127            // Play with the horizontal scrollbar
128            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
129            term.set_hscrollbar_style(ScrollbarStyle::ON);
130            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
131
132            // Test show_unknown() as incidental part of testing append methods
133            term.set_show_unknown(true);
134            assert!(term.show_unknown());
135            term.append_ascii(
136                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
137            );
138            term.set_show_unknown(false);
139            assert!(!term.show_unknown());
140
141            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
142            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
143
144            let r = term.cursor_row();
145            assert_eq!(term.cursor_col(), 0);
146            term.append(&format!("Testing cursor row/col {r}"));
147            assert_eq!(term.cursor_col(), 24);
148            assert_eq!(term.cursor_row(), r);
149
150            // Test cursor color methods
151            assert_eq!(
152                term.cursor_bg_color(),
153                Color::XtermGreen,
154                "Default cursor bg at startup"
155            );
156            assert_eq!(
157                term.cursor_fg_color(),
158                Color::from_hex(0xff_ff_f0),
159                "Default cursor fg at startup"
160            );
161            term.set_cursor_bg_color(Color::Red);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
164            term.set_cursor_fg_color(Color::Blue);
165            assert_eq!(term.cursor_bg_color(), Color::Red);
166            assert_eq!(term.cursor_fg_color(), Color::Blue);
167            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
168            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
169            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
170            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
171
172            // The default display_rows() will derive from the window size
173            let dr = term.display_rows();
174            let height = term.height();
175            assert_eq!(height, term.h());
176            assert!(dr > 20, "Default display_rows at startup");
177            term.resize(term.x(), term.y(), term.w(), height * 2);
178            assert_eq!(term.h(), height * 2);
179            assert_eq!(height * 2, term.h());
180            assert!(term.display_rows() > dr);
181            term.resize(term.x(), term.y(), term.w(), height);
182
183            // The default display_columns() will derive from the window size
184            let dc = term.display_columns();
185            assert!(dc > 80, "Default display_rows at startup");
186            term.set_display_columns(200);
187            assert_eq!(term.display_columns(), 200);
188            term.append("\n         1         2         3         4         5         6         7         8         9");
189            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
190            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
191            term.set_display_columns(90);
192            assert_eq!(term.display_columns(), 90);
193            term.set_display_columns(dc); // Set back to default
194            assert_eq!(term.display_columns(), dc);
195
196            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
197            term.set_history_rows(50);
198            assert_eq!(term.history_rows(), 50);
199            term.set_history_rows(100); // Set back to default
200            assert_eq!(term.history_rows(), 100);
201
202            let hu = term.history_use();
203            term.append(&format!(
204                "history_use = {hu} (it's not clear what this means)\n"
205            ));
206            // assert_eq!(term.history_use(), hu+1);
207
208            term.append(&format!(
209                "margins = b:{} l:{} r:{} t{}\n",
210                term.margin_bottom(),
211                term.margin_left(),
212                term.margin_right(),
213                term.margin_top()
214            ));
215            assert_eq!(term.margin_bottom(), 3);
216            assert_eq!(term.margin_left(), 3);
217            assert_eq!(term.margin_right(), 3);
218            assert_eq!(term.margin_top(), 3);
219
220            term.set_margin_bottom(5);
221            term.set_margin_left(10);
222            term.set_margin_right(15);
223            term.set_margin_top(20);
224            assert_eq!(term.margin_bottom(), 5);
225            assert_eq!(term.margin_left(), 10);
226            assert_eq!(term.margin_right(), 15);
227            assert_eq!(term.margin_top(), 20);
228
229            term.append("Single character: '");
230            term.print_char('X');
231            term.append("', single UTF-8 character: '");
232            term.print_char_utf8('↑');
233            term.append("'\n");
234
235            let rr = term.redraw_rate();
236            assert_eq!(rr, 0.1, "Default redraw rate at startup");
237            term.append(&format!("Redraw rate {rr}\n"));
238            term.set_redraw_rate(1.0);
239            assert_eq!(term.redraw_rate(), 1.0);
240            term.set_redraw_rate(rr);
241            assert_eq!(term.redraw_rate(), rr);
242
243            let rs = term.redraw_style();
244            term.append(&format!("Redraw style {rs:?}\n"));
245            assert_eq!(
246                rs,
247                RedrawStyle::RateLimited,
248                "Default redraw style at startup"
249            );
250            term.set_redraw_style(RedrawStyle::NoRedraw);
251            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
252            term.set_redraw_style(rs);
253            assert_eq!(term.redraw_style(), rs);
254
255            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
256            assert_eq!(
257                RedrawStyle::NoRedraw.bits(),
258                0x0000,
259                "RedrawStyle enum values have been reassigned"
260            );
261            assert_eq!(
262                RedrawStyle::RateLimited.bits(),
263                0x0001,
264                "RedrawStyle enum values have been reassigned"
265            );
266            assert_eq!(
267                RedrawStyle::PerWrite.bits(),
268                0x0002,
269                "RedrawStyle enum values have been reassigned"
270            );
271
272            let sb = term.scrollbar();
273            let hsb = term.hscrollbar();
274            // Both vertical and horizontal scrollbars are at zero
275            assert_eq!(sb.value(), 0.0);
276            assert_eq!(hsb.value(), 0.0);
277            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
278
279            term.append(&format!(
280                "Scrollbar actual size {}\n",
281                term.scrollbar_actual_size()
282            ));
283            assert_eq!(term.scrollbar_actual_size(), 16);
284            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
285            assert_eq!(
286                term.scrollbar_size(),
287                0,
288                "Default scrollbar size at startup"
289            );
290            term.set_scrollbar_size(40);
291            assert_eq!(term.scrollbar_size(), 40);
292            assert_eq!(term.scrollbar_actual_size(), 40);
293            term.append(&format!(
294                "Scrollbar actual size {}\n",
295                term.scrollbar_actual_size()
296            ));
297            term.set_scrollbar_size(0); // Restore default
298            assert_eq!(term.scrollbar_size(), 0);
299            assert_eq!(term.scrollbar_actual_size(), 16);
300
301            let sfc = term.selection_fg_color();
302            let sbc = term.selection_bg_color();
303            assert_eq!(sfc, Color::Black);
304            assert_eq!(sbc, Color::White);
305            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
306            term.set_selection_fg_color(Color::Green);
307            term.set_selection_bg_color(Color::DarkBlue);
308            assert_eq!(term.selection_fg_color(), Color::Green);
309            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
310            term.set_selection_fg_color(sfc);
311            term.set_selection_bg_color(sbc);
312            assert_eq!(term.selection_fg_color(), Color::Black);
313            assert_eq!(term.selection_bg_color(), Color::White);
314
315            let tfcd = term.text_fg_color_default();
316            let tbcd = term.text_bg_color_default();
317            assert_eq!(tfcd, Color::XtermWhite);
318            assert_eq!(tbcd, Color::TransparentBg);
319            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
320            term.set_text_fg_color_default(Color::Green);
321            term.set_text_bg_color_default(Color::DarkBlue);
322            assert_eq!(term.text_fg_color_default(), Color::Green);
323            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
324            term.set_text_fg_color_default(tfcd);
325            term.set_text_bg_color_default(tbcd);
326            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
327            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
328
329            let tfc = term.text_fg_color();
330            let tbc = term.text_bg_color();
331            assert_eq!(tfc, Color::XtermWhite);
332            assert_eq!(tbc, Color::TransparentBg);
333            term.append(&format!("Text colors: {sfc} {sbc}\n"));
334            term.set_text_fg_color(Color::Green);
335            term.set_text_bg_color(Color::DarkBlue);
336            assert_eq!(term.text_fg_color(), Color::Green);
337            assert_eq!(term.text_bg_color(), Color::DarkBlue);
338            term.set_text_fg_color(tfc);
339            term.set_text_bg_color(tbc);
340            assert_eq!(term.text_fg_color(), Color::XtermWhite);
341            assert_eq!(term.text_bg_color(), Color::TransparentBg);
342
343            let tf = term.text_font();
344            term.append(&format!("Text font: {tf:?}\n"));
345            assert_eq!(tf, Font::Courier);
346            term.set_text_font(Font::Screen);
347            assert_eq!(term.text_font(), Font::Screen);
348            term.set_text_font(tf);
349            assert_eq!(term.text_font(), Font::Courier);
350
351            let ts = term.text_size();
352            let r = term.h_to_row(100);
353            let c = term.w_to_col(100);
354            term.append(&format!(
355                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
356            ));
357            assert_eq!(ts, 14);
358            term.set_text_size(30);
359            assert_eq!(term.text_size(), 30);
360            term.append(&format!(
361                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
362                term.text_size(),
363                term.h_to_row(100),
364                term.w_to_col(100)
365            ));
366            term.set_text_size(ts);
367            assert_eq!(term.text_size(), ts);
368            term.append(&format!(
369                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
370                term.text_size(),
371                term.h_to_row(100),
372                term.w_to_col(100)
373            ));
374
375            // Keyboard handler
376            term.handle({
377                move |term, e| {
378                    match e {
379                        fltk::enums::Event::KeyDown
380                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
381                        {
382                            // false to let FLTK handle ESC. true to hide ESC
383                            false
384                        }
385
386                        fltk::enums::Event::KeyDown
387                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
388                        {
389                            // We handle control keystroke
390                            let k = fltk::app::event_text();
391                            term.append_utf8(&k);
392                            true
393                        }
394
395                        fltk::enums::Event::KeyDown
396                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
397                        {
398                            // We handle normal printable keystroke
399                            let k = fltk::app::event_text();
400                            term.take_focus().unwrap();
401                            term.append(&k);
402                            true
403                        }
404
405                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
406                        // We can do this, or else ignore them (return false)
407                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
408                        //     term.redraw();
409                        //     true
410                        // }
411                        _ => false, // Let FLTK handle everything else
412                    }
413                }
414            });
415
416            let attr_save = term.text_attrib();
417            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
418            term.append("\nStartup tests complete. Keyboard is live.\n");
419            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
420            term.set_text_attrib(attr_save);
421            assert_eq!(term.text_attrib(), attr_save);
422            term.redraw();
423        }
424    });
425
426    app.run().unwrap();
427}
Source

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

Set the top margin

Examples found in repository?
examples/terminal.rs (line 223)
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!(term.text(true), "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");
115
116            // Testing ansi() and set_ansi() methods
117            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
118            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
119            term.set_ansi(false);
120            assert!(!term.ansi());
121            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
122            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
123            term.append_u8(b"Appending u8 array\n");
124            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
125            term.set_ansi(true); // Restore ANSI state
126
127            // Play with the horizontal scrollbar
128            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
129            term.set_hscrollbar_style(ScrollbarStyle::ON);
130            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
131
132            // Test show_unknown() as incidental part of testing append methods
133            term.set_show_unknown(true);
134            assert!(term.show_unknown());
135            term.append_ascii(
136                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
137            );
138            term.set_show_unknown(false);
139            assert!(!term.show_unknown());
140
141            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
142            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
143
144            let r = term.cursor_row();
145            assert_eq!(term.cursor_col(), 0);
146            term.append(&format!("Testing cursor row/col {r}"));
147            assert_eq!(term.cursor_col(), 24);
148            assert_eq!(term.cursor_row(), r);
149
150            // Test cursor color methods
151            assert_eq!(
152                term.cursor_bg_color(),
153                Color::XtermGreen,
154                "Default cursor bg at startup"
155            );
156            assert_eq!(
157                term.cursor_fg_color(),
158                Color::from_hex(0xff_ff_f0),
159                "Default cursor fg at startup"
160            );
161            term.set_cursor_bg_color(Color::Red);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
164            term.set_cursor_fg_color(Color::Blue);
165            assert_eq!(term.cursor_bg_color(), Color::Red);
166            assert_eq!(term.cursor_fg_color(), Color::Blue);
167            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
168            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
169            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
170            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
171
172            // The default display_rows() will derive from the window size
173            let dr = term.display_rows();
174            let height = term.height();
175            assert_eq!(height, term.h());
176            assert!(dr > 20, "Default display_rows at startup");
177            term.resize(term.x(), term.y(), term.w(), height * 2);
178            assert_eq!(term.h(), height * 2);
179            assert_eq!(height * 2, term.h());
180            assert!(term.display_rows() > dr);
181            term.resize(term.x(), term.y(), term.w(), height);
182
183            // The default display_columns() will derive from the window size
184            let dc = term.display_columns();
185            assert!(dc > 80, "Default display_rows at startup");
186            term.set_display_columns(200);
187            assert_eq!(term.display_columns(), 200);
188            term.append("\n         1         2         3         4         5         6         7         8         9");
189            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
190            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
191            term.set_display_columns(90);
192            assert_eq!(term.display_columns(), 90);
193            term.set_display_columns(dc); // Set back to default
194            assert_eq!(term.display_columns(), dc);
195
196            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
197            term.set_history_rows(50);
198            assert_eq!(term.history_rows(), 50);
199            term.set_history_rows(100); // Set back to default
200            assert_eq!(term.history_rows(), 100);
201
202            let hu = term.history_use();
203            term.append(&format!(
204                "history_use = {hu} (it's not clear what this means)\n"
205            ));
206            // assert_eq!(term.history_use(), hu+1);
207
208            term.append(&format!(
209                "margins = b:{} l:{} r:{} t{}\n",
210                term.margin_bottom(),
211                term.margin_left(),
212                term.margin_right(),
213                term.margin_top()
214            ));
215            assert_eq!(term.margin_bottom(), 3);
216            assert_eq!(term.margin_left(), 3);
217            assert_eq!(term.margin_right(), 3);
218            assert_eq!(term.margin_top(), 3);
219
220            term.set_margin_bottom(5);
221            term.set_margin_left(10);
222            term.set_margin_right(15);
223            term.set_margin_top(20);
224            assert_eq!(term.margin_bottom(), 5);
225            assert_eq!(term.margin_left(), 10);
226            assert_eq!(term.margin_right(), 15);
227            assert_eq!(term.margin_top(), 20);
228
229            term.append("Single character: '");
230            term.print_char('X');
231            term.append("', single UTF-8 character: '");
232            term.print_char_utf8('↑');
233            term.append("'\n");
234
235            let rr = term.redraw_rate();
236            assert_eq!(rr, 0.1, "Default redraw rate at startup");
237            term.append(&format!("Redraw rate {rr}\n"));
238            term.set_redraw_rate(1.0);
239            assert_eq!(term.redraw_rate(), 1.0);
240            term.set_redraw_rate(rr);
241            assert_eq!(term.redraw_rate(), rr);
242
243            let rs = term.redraw_style();
244            term.append(&format!("Redraw style {rs:?}\n"));
245            assert_eq!(
246                rs,
247                RedrawStyle::RateLimited,
248                "Default redraw style at startup"
249            );
250            term.set_redraw_style(RedrawStyle::NoRedraw);
251            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
252            term.set_redraw_style(rs);
253            assert_eq!(term.redraw_style(), rs);
254
255            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
256            assert_eq!(
257                RedrawStyle::NoRedraw.bits(),
258                0x0000,
259                "RedrawStyle enum values have been reassigned"
260            );
261            assert_eq!(
262                RedrawStyle::RateLimited.bits(),
263                0x0001,
264                "RedrawStyle enum values have been reassigned"
265            );
266            assert_eq!(
267                RedrawStyle::PerWrite.bits(),
268                0x0002,
269                "RedrawStyle enum values have been reassigned"
270            );
271
272            let sb = term.scrollbar();
273            let hsb = term.hscrollbar();
274            // Both vertical and horizontal scrollbars are at zero
275            assert_eq!(sb.value(), 0.0);
276            assert_eq!(hsb.value(), 0.0);
277            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
278
279            term.append(&format!(
280                "Scrollbar actual size {}\n",
281                term.scrollbar_actual_size()
282            ));
283            assert_eq!(term.scrollbar_actual_size(), 16);
284            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
285            assert_eq!(
286                term.scrollbar_size(),
287                0,
288                "Default scrollbar size at startup"
289            );
290            term.set_scrollbar_size(40);
291            assert_eq!(term.scrollbar_size(), 40);
292            assert_eq!(term.scrollbar_actual_size(), 40);
293            term.append(&format!(
294                "Scrollbar actual size {}\n",
295                term.scrollbar_actual_size()
296            ));
297            term.set_scrollbar_size(0); // Restore default
298            assert_eq!(term.scrollbar_size(), 0);
299            assert_eq!(term.scrollbar_actual_size(), 16);
300
301            let sfc = term.selection_fg_color();
302            let sbc = term.selection_bg_color();
303            assert_eq!(sfc, Color::Black);
304            assert_eq!(sbc, Color::White);
305            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
306            term.set_selection_fg_color(Color::Green);
307            term.set_selection_bg_color(Color::DarkBlue);
308            assert_eq!(term.selection_fg_color(), Color::Green);
309            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
310            term.set_selection_fg_color(sfc);
311            term.set_selection_bg_color(sbc);
312            assert_eq!(term.selection_fg_color(), Color::Black);
313            assert_eq!(term.selection_bg_color(), Color::White);
314
315            let tfcd = term.text_fg_color_default();
316            let tbcd = term.text_bg_color_default();
317            assert_eq!(tfcd, Color::XtermWhite);
318            assert_eq!(tbcd, Color::TransparentBg);
319            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
320            term.set_text_fg_color_default(Color::Green);
321            term.set_text_bg_color_default(Color::DarkBlue);
322            assert_eq!(term.text_fg_color_default(), Color::Green);
323            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
324            term.set_text_fg_color_default(tfcd);
325            term.set_text_bg_color_default(tbcd);
326            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
327            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
328
329            let tfc = term.text_fg_color();
330            let tbc = term.text_bg_color();
331            assert_eq!(tfc, Color::XtermWhite);
332            assert_eq!(tbc, Color::TransparentBg);
333            term.append(&format!("Text colors: {sfc} {sbc}\n"));
334            term.set_text_fg_color(Color::Green);
335            term.set_text_bg_color(Color::DarkBlue);
336            assert_eq!(term.text_fg_color(), Color::Green);
337            assert_eq!(term.text_bg_color(), Color::DarkBlue);
338            term.set_text_fg_color(tfc);
339            term.set_text_bg_color(tbc);
340            assert_eq!(term.text_fg_color(), Color::XtermWhite);
341            assert_eq!(term.text_bg_color(), Color::TransparentBg);
342
343            let tf = term.text_font();
344            term.append(&format!("Text font: {tf:?}\n"));
345            assert_eq!(tf, Font::Courier);
346            term.set_text_font(Font::Screen);
347            assert_eq!(term.text_font(), Font::Screen);
348            term.set_text_font(tf);
349            assert_eq!(term.text_font(), Font::Courier);
350
351            let ts = term.text_size();
352            let r = term.h_to_row(100);
353            let c = term.w_to_col(100);
354            term.append(&format!(
355                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
356            ));
357            assert_eq!(ts, 14);
358            term.set_text_size(30);
359            assert_eq!(term.text_size(), 30);
360            term.append(&format!(
361                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
362                term.text_size(),
363                term.h_to_row(100),
364                term.w_to_col(100)
365            ));
366            term.set_text_size(ts);
367            assert_eq!(term.text_size(), ts);
368            term.append(&format!(
369                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
370                term.text_size(),
371                term.h_to_row(100),
372                term.w_to_col(100)
373            ));
374
375            // Keyboard handler
376            term.handle({
377                move |term, e| {
378                    match e {
379                        fltk::enums::Event::KeyDown
380                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
381                        {
382                            // false to let FLTK handle ESC. true to hide ESC
383                            false
384                        }
385
386                        fltk::enums::Event::KeyDown
387                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
388                        {
389                            // We handle control keystroke
390                            let k = fltk::app::event_text();
391                            term.append_utf8(&k);
392                            true
393                        }
394
395                        fltk::enums::Event::KeyDown
396                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
397                        {
398                            // We handle normal printable keystroke
399                            let k = fltk::app::event_text();
400                            term.take_focus().unwrap();
401                            term.append(&k);
402                            true
403                        }
404
405                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
406                        // We can do this, or else ignore them (return false)
407                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
408                        //     term.redraw();
409                        //     true
410                        // }
411                        _ => false, // Let FLTK handle everything else
412                    }
413                }
414            });
415
416            let attr_save = term.text_attrib();
417            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
418            term.append("\nStartup tests complete. Keyboard is live.\n");
419            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
420            term.set_text_attrib(attr_save);
421            assert_eq!(term.text_attrib(), attr_save);
422            term.redraw();
423        }
424    });
425
426    app.run().unwrap();
427}
Source

pub fn margin_top(&self) -> i32

Return the top margin

Examples found in repository?
examples/terminal.rs (line 213)
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!(term.text(true), "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");
115
116            // Testing ansi() and set_ansi() methods
117            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
118            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
119            term.set_ansi(false);
120            assert!(!term.ansi());
121            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
122            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
123            term.append_u8(b"Appending u8 array\n");
124            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
125            term.set_ansi(true); // Restore ANSI state
126
127            // Play with the horizontal scrollbar
128            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
129            term.set_hscrollbar_style(ScrollbarStyle::ON);
130            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
131
132            // Test show_unknown() as incidental part of testing append methods
133            term.set_show_unknown(true);
134            assert!(term.show_unknown());
135            term.append_ascii(
136                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
137            );
138            term.set_show_unknown(false);
139            assert!(!term.show_unknown());
140
141            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
142            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
143
144            let r = term.cursor_row();
145            assert_eq!(term.cursor_col(), 0);
146            term.append(&format!("Testing cursor row/col {r}"));
147            assert_eq!(term.cursor_col(), 24);
148            assert_eq!(term.cursor_row(), r);
149
150            // Test cursor color methods
151            assert_eq!(
152                term.cursor_bg_color(),
153                Color::XtermGreen,
154                "Default cursor bg at startup"
155            );
156            assert_eq!(
157                term.cursor_fg_color(),
158                Color::from_hex(0xff_ff_f0),
159                "Default cursor fg at startup"
160            );
161            term.set_cursor_bg_color(Color::Red);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
164            term.set_cursor_fg_color(Color::Blue);
165            assert_eq!(term.cursor_bg_color(), Color::Red);
166            assert_eq!(term.cursor_fg_color(), Color::Blue);
167            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
168            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
169            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
170            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
171
172            // The default display_rows() will derive from the window size
173            let dr = term.display_rows();
174            let height = term.height();
175            assert_eq!(height, term.h());
176            assert!(dr > 20, "Default display_rows at startup");
177            term.resize(term.x(), term.y(), term.w(), height * 2);
178            assert_eq!(term.h(), height * 2);
179            assert_eq!(height * 2, term.h());
180            assert!(term.display_rows() > dr);
181            term.resize(term.x(), term.y(), term.w(), height);
182
183            // The default display_columns() will derive from the window size
184            let dc = term.display_columns();
185            assert!(dc > 80, "Default display_rows at startup");
186            term.set_display_columns(200);
187            assert_eq!(term.display_columns(), 200);
188            term.append("\n         1         2         3         4         5         6         7         8         9");
189            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
190            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
191            term.set_display_columns(90);
192            assert_eq!(term.display_columns(), 90);
193            term.set_display_columns(dc); // Set back to default
194            assert_eq!(term.display_columns(), dc);
195
196            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
197            term.set_history_rows(50);
198            assert_eq!(term.history_rows(), 50);
199            term.set_history_rows(100); // Set back to default
200            assert_eq!(term.history_rows(), 100);
201
202            let hu = term.history_use();
203            term.append(&format!(
204                "history_use = {hu} (it's not clear what this means)\n"
205            ));
206            // assert_eq!(term.history_use(), hu+1);
207
208            term.append(&format!(
209                "margins = b:{} l:{} r:{} t{}\n",
210                term.margin_bottom(),
211                term.margin_left(),
212                term.margin_right(),
213                term.margin_top()
214            ));
215            assert_eq!(term.margin_bottom(), 3);
216            assert_eq!(term.margin_left(), 3);
217            assert_eq!(term.margin_right(), 3);
218            assert_eq!(term.margin_top(), 3);
219
220            term.set_margin_bottom(5);
221            term.set_margin_left(10);
222            term.set_margin_right(15);
223            term.set_margin_top(20);
224            assert_eq!(term.margin_bottom(), 5);
225            assert_eq!(term.margin_left(), 10);
226            assert_eq!(term.margin_right(), 15);
227            assert_eq!(term.margin_top(), 20);
228
229            term.append("Single character: '");
230            term.print_char('X');
231            term.append("', single UTF-8 character: '");
232            term.print_char_utf8('↑');
233            term.append("'\n");
234
235            let rr = term.redraw_rate();
236            assert_eq!(rr, 0.1, "Default redraw rate at startup");
237            term.append(&format!("Redraw rate {rr}\n"));
238            term.set_redraw_rate(1.0);
239            assert_eq!(term.redraw_rate(), 1.0);
240            term.set_redraw_rate(rr);
241            assert_eq!(term.redraw_rate(), rr);
242
243            let rs = term.redraw_style();
244            term.append(&format!("Redraw style {rs:?}\n"));
245            assert_eq!(
246                rs,
247                RedrawStyle::RateLimited,
248                "Default redraw style at startup"
249            );
250            term.set_redraw_style(RedrawStyle::NoRedraw);
251            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
252            term.set_redraw_style(rs);
253            assert_eq!(term.redraw_style(), rs);
254
255            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
256            assert_eq!(
257                RedrawStyle::NoRedraw.bits(),
258                0x0000,
259                "RedrawStyle enum values have been reassigned"
260            );
261            assert_eq!(
262                RedrawStyle::RateLimited.bits(),
263                0x0001,
264                "RedrawStyle enum values have been reassigned"
265            );
266            assert_eq!(
267                RedrawStyle::PerWrite.bits(),
268                0x0002,
269                "RedrawStyle enum values have been reassigned"
270            );
271
272            let sb = term.scrollbar();
273            let hsb = term.hscrollbar();
274            // Both vertical and horizontal scrollbars are at zero
275            assert_eq!(sb.value(), 0.0);
276            assert_eq!(hsb.value(), 0.0);
277            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
278
279            term.append(&format!(
280                "Scrollbar actual size {}\n",
281                term.scrollbar_actual_size()
282            ));
283            assert_eq!(term.scrollbar_actual_size(), 16);
284            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
285            assert_eq!(
286                term.scrollbar_size(),
287                0,
288                "Default scrollbar size at startup"
289            );
290            term.set_scrollbar_size(40);
291            assert_eq!(term.scrollbar_size(), 40);
292            assert_eq!(term.scrollbar_actual_size(), 40);
293            term.append(&format!(
294                "Scrollbar actual size {}\n",
295                term.scrollbar_actual_size()
296            ));
297            term.set_scrollbar_size(0); // Restore default
298            assert_eq!(term.scrollbar_size(), 0);
299            assert_eq!(term.scrollbar_actual_size(), 16);
300
301            let sfc = term.selection_fg_color();
302            let sbc = term.selection_bg_color();
303            assert_eq!(sfc, Color::Black);
304            assert_eq!(sbc, Color::White);
305            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
306            term.set_selection_fg_color(Color::Green);
307            term.set_selection_bg_color(Color::DarkBlue);
308            assert_eq!(term.selection_fg_color(), Color::Green);
309            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
310            term.set_selection_fg_color(sfc);
311            term.set_selection_bg_color(sbc);
312            assert_eq!(term.selection_fg_color(), Color::Black);
313            assert_eq!(term.selection_bg_color(), Color::White);
314
315            let tfcd = term.text_fg_color_default();
316            let tbcd = term.text_bg_color_default();
317            assert_eq!(tfcd, Color::XtermWhite);
318            assert_eq!(tbcd, Color::TransparentBg);
319            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
320            term.set_text_fg_color_default(Color::Green);
321            term.set_text_bg_color_default(Color::DarkBlue);
322            assert_eq!(term.text_fg_color_default(), Color::Green);
323            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
324            term.set_text_fg_color_default(tfcd);
325            term.set_text_bg_color_default(tbcd);
326            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
327            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
328
329            let tfc = term.text_fg_color();
330            let tbc = term.text_bg_color();
331            assert_eq!(tfc, Color::XtermWhite);
332            assert_eq!(tbc, Color::TransparentBg);
333            term.append(&format!("Text colors: {sfc} {sbc}\n"));
334            term.set_text_fg_color(Color::Green);
335            term.set_text_bg_color(Color::DarkBlue);
336            assert_eq!(term.text_fg_color(), Color::Green);
337            assert_eq!(term.text_bg_color(), Color::DarkBlue);
338            term.set_text_fg_color(tfc);
339            term.set_text_bg_color(tbc);
340            assert_eq!(term.text_fg_color(), Color::XtermWhite);
341            assert_eq!(term.text_bg_color(), Color::TransparentBg);
342
343            let tf = term.text_font();
344            term.append(&format!("Text font: {tf:?}\n"));
345            assert_eq!(tf, Font::Courier);
346            term.set_text_font(Font::Screen);
347            assert_eq!(term.text_font(), Font::Screen);
348            term.set_text_font(tf);
349            assert_eq!(term.text_font(), Font::Courier);
350
351            let ts = term.text_size();
352            let r = term.h_to_row(100);
353            let c = term.w_to_col(100);
354            term.append(&format!(
355                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
356            ));
357            assert_eq!(ts, 14);
358            term.set_text_size(30);
359            assert_eq!(term.text_size(), 30);
360            term.append(&format!(
361                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
362                term.text_size(),
363                term.h_to_row(100),
364                term.w_to_col(100)
365            ));
366            term.set_text_size(ts);
367            assert_eq!(term.text_size(), ts);
368            term.append(&format!(
369                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
370                term.text_size(),
371                term.h_to_row(100),
372                term.w_to_col(100)
373            ));
374
375            // Keyboard handler
376            term.handle({
377                move |term, e| {
378                    match e {
379                        fltk::enums::Event::KeyDown
380                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
381                        {
382                            // false to let FLTK handle ESC. true to hide ESC
383                            false
384                        }
385
386                        fltk::enums::Event::KeyDown
387                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
388                        {
389                            // We handle control keystroke
390                            let k = fltk::app::event_text();
391                            term.append_utf8(&k);
392                            true
393                        }
394
395                        fltk::enums::Event::KeyDown
396                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
397                        {
398                            // We handle normal printable keystroke
399                            let k = fltk::app::event_text();
400                            term.take_focus().unwrap();
401                            term.append(&k);
402                            true
403                        }
404
405                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
406                        // We can do this, or else ignore them (return false)
407                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
408                        //     term.redraw();
409                        //     true
410                        // }
411                        _ => false, // Let FLTK handle everything else
412                    }
413                }
414            });
415
416            let attr_save = term.text_attrib();
417            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
418            term.append("\nStartup tests complete. Keyboard is live.\n");
419            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
420            term.set_text_attrib(attr_save);
421            assert_eq!(term.text_attrib(), attr_save);
422            term.redraw();
423        }
424    });
425
426    app.run().unwrap();
427}
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 352)
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!(term.text(true), "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");
115
116            // Testing ansi() and set_ansi() methods
117            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
118            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
119            term.set_ansi(false);
120            assert!(!term.ansi());
121            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
122            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
123            term.append_u8(b"Appending u8 array\n");
124            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
125            term.set_ansi(true); // Restore ANSI state
126
127            // Play with the horizontal scrollbar
128            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
129            term.set_hscrollbar_style(ScrollbarStyle::ON);
130            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
131
132            // Test show_unknown() as incidental part of testing append methods
133            term.set_show_unknown(true);
134            assert!(term.show_unknown());
135            term.append_ascii(
136                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
137            );
138            term.set_show_unknown(false);
139            assert!(!term.show_unknown());
140
141            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
142            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
143
144            let r = term.cursor_row();
145            assert_eq!(term.cursor_col(), 0);
146            term.append(&format!("Testing cursor row/col {r}"));
147            assert_eq!(term.cursor_col(), 24);
148            assert_eq!(term.cursor_row(), r);
149
150            // Test cursor color methods
151            assert_eq!(
152                term.cursor_bg_color(),
153                Color::XtermGreen,
154                "Default cursor bg at startup"
155            );
156            assert_eq!(
157                term.cursor_fg_color(),
158                Color::from_hex(0xff_ff_f0),
159                "Default cursor fg at startup"
160            );
161            term.set_cursor_bg_color(Color::Red);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
164            term.set_cursor_fg_color(Color::Blue);
165            assert_eq!(term.cursor_bg_color(), Color::Red);
166            assert_eq!(term.cursor_fg_color(), Color::Blue);
167            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
168            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
169            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
170            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
171
172            // The default display_rows() will derive from the window size
173            let dr = term.display_rows();
174            let height = term.height();
175            assert_eq!(height, term.h());
176            assert!(dr > 20, "Default display_rows at startup");
177            term.resize(term.x(), term.y(), term.w(), height * 2);
178            assert_eq!(term.h(), height * 2);
179            assert_eq!(height * 2, term.h());
180            assert!(term.display_rows() > dr);
181            term.resize(term.x(), term.y(), term.w(), height);
182
183            // The default display_columns() will derive from the window size
184            let dc = term.display_columns();
185            assert!(dc > 80, "Default display_rows at startup");
186            term.set_display_columns(200);
187            assert_eq!(term.display_columns(), 200);
188            term.append("\n         1         2         3         4         5         6         7         8         9");
189            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
190            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
191            term.set_display_columns(90);
192            assert_eq!(term.display_columns(), 90);
193            term.set_display_columns(dc); // Set back to default
194            assert_eq!(term.display_columns(), dc);
195
196            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
197            term.set_history_rows(50);
198            assert_eq!(term.history_rows(), 50);
199            term.set_history_rows(100); // Set back to default
200            assert_eq!(term.history_rows(), 100);
201
202            let hu = term.history_use();
203            term.append(&format!(
204                "history_use = {hu} (it's not clear what this means)\n"
205            ));
206            // assert_eq!(term.history_use(), hu+1);
207
208            term.append(&format!(
209                "margins = b:{} l:{} r:{} t{}\n",
210                term.margin_bottom(),
211                term.margin_left(),
212                term.margin_right(),
213                term.margin_top()
214            ));
215            assert_eq!(term.margin_bottom(), 3);
216            assert_eq!(term.margin_left(), 3);
217            assert_eq!(term.margin_right(), 3);
218            assert_eq!(term.margin_top(), 3);
219
220            term.set_margin_bottom(5);
221            term.set_margin_left(10);
222            term.set_margin_right(15);
223            term.set_margin_top(20);
224            assert_eq!(term.margin_bottom(), 5);
225            assert_eq!(term.margin_left(), 10);
226            assert_eq!(term.margin_right(), 15);
227            assert_eq!(term.margin_top(), 20);
228
229            term.append("Single character: '");
230            term.print_char('X');
231            term.append("', single UTF-8 character: '");
232            term.print_char_utf8('↑');
233            term.append("'\n");
234
235            let rr = term.redraw_rate();
236            assert_eq!(rr, 0.1, "Default redraw rate at startup");
237            term.append(&format!("Redraw rate {rr}\n"));
238            term.set_redraw_rate(1.0);
239            assert_eq!(term.redraw_rate(), 1.0);
240            term.set_redraw_rate(rr);
241            assert_eq!(term.redraw_rate(), rr);
242
243            let rs = term.redraw_style();
244            term.append(&format!("Redraw style {rs:?}\n"));
245            assert_eq!(
246                rs,
247                RedrawStyle::RateLimited,
248                "Default redraw style at startup"
249            );
250            term.set_redraw_style(RedrawStyle::NoRedraw);
251            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
252            term.set_redraw_style(rs);
253            assert_eq!(term.redraw_style(), rs);
254
255            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
256            assert_eq!(
257                RedrawStyle::NoRedraw.bits(),
258                0x0000,
259                "RedrawStyle enum values have been reassigned"
260            );
261            assert_eq!(
262                RedrawStyle::RateLimited.bits(),
263                0x0001,
264                "RedrawStyle enum values have been reassigned"
265            );
266            assert_eq!(
267                RedrawStyle::PerWrite.bits(),
268                0x0002,
269                "RedrawStyle enum values have been reassigned"
270            );
271
272            let sb = term.scrollbar();
273            let hsb = term.hscrollbar();
274            // Both vertical and horizontal scrollbars are at zero
275            assert_eq!(sb.value(), 0.0);
276            assert_eq!(hsb.value(), 0.0);
277            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
278
279            term.append(&format!(
280                "Scrollbar actual size {}\n",
281                term.scrollbar_actual_size()
282            ));
283            assert_eq!(term.scrollbar_actual_size(), 16);
284            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
285            assert_eq!(
286                term.scrollbar_size(),
287                0,
288                "Default scrollbar size at startup"
289            );
290            term.set_scrollbar_size(40);
291            assert_eq!(term.scrollbar_size(), 40);
292            assert_eq!(term.scrollbar_actual_size(), 40);
293            term.append(&format!(
294                "Scrollbar actual size {}\n",
295                term.scrollbar_actual_size()
296            ));
297            term.set_scrollbar_size(0); // Restore default
298            assert_eq!(term.scrollbar_size(), 0);
299            assert_eq!(term.scrollbar_actual_size(), 16);
300
301            let sfc = term.selection_fg_color();
302            let sbc = term.selection_bg_color();
303            assert_eq!(sfc, Color::Black);
304            assert_eq!(sbc, Color::White);
305            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
306            term.set_selection_fg_color(Color::Green);
307            term.set_selection_bg_color(Color::DarkBlue);
308            assert_eq!(term.selection_fg_color(), Color::Green);
309            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
310            term.set_selection_fg_color(sfc);
311            term.set_selection_bg_color(sbc);
312            assert_eq!(term.selection_fg_color(), Color::Black);
313            assert_eq!(term.selection_bg_color(), Color::White);
314
315            let tfcd = term.text_fg_color_default();
316            let tbcd = term.text_bg_color_default();
317            assert_eq!(tfcd, Color::XtermWhite);
318            assert_eq!(tbcd, Color::TransparentBg);
319            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
320            term.set_text_fg_color_default(Color::Green);
321            term.set_text_bg_color_default(Color::DarkBlue);
322            assert_eq!(term.text_fg_color_default(), Color::Green);
323            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
324            term.set_text_fg_color_default(tfcd);
325            term.set_text_bg_color_default(tbcd);
326            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
327            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
328
329            let tfc = term.text_fg_color();
330            let tbc = term.text_bg_color();
331            assert_eq!(tfc, Color::XtermWhite);
332            assert_eq!(tbc, Color::TransparentBg);
333            term.append(&format!("Text colors: {sfc} {sbc}\n"));
334            term.set_text_fg_color(Color::Green);
335            term.set_text_bg_color(Color::DarkBlue);
336            assert_eq!(term.text_fg_color(), Color::Green);
337            assert_eq!(term.text_bg_color(), Color::DarkBlue);
338            term.set_text_fg_color(tfc);
339            term.set_text_bg_color(tbc);
340            assert_eq!(term.text_fg_color(), Color::XtermWhite);
341            assert_eq!(term.text_bg_color(), Color::TransparentBg);
342
343            let tf = term.text_font();
344            term.append(&format!("Text font: {tf:?}\n"));
345            assert_eq!(tf, Font::Courier);
346            term.set_text_font(Font::Screen);
347            assert_eq!(term.text_font(), Font::Screen);
348            term.set_text_font(tf);
349            assert_eq!(term.text_font(), Font::Courier);
350
351            let ts = term.text_size();
352            let r = term.h_to_row(100);
353            let c = term.w_to_col(100);
354            term.append(&format!(
355                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
356            ));
357            assert_eq!(ts, 14);
358            term.set_text_size(30);
359            assert_eq!(term.text_size(), 30);
360            term.append(&format!(
361                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
362                term.text_size(),
363                term.h_to_row(100),
364                term.w_to_col(100)
365            ));
366            term.set_text_size(ts);
367            assert_eq!(term.text_size(), ts);
368            term.append(&format!(
369                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
370                term.text_size(),
371                term.h_to_row(100),
372                term.w_to_col(100)
373            ));
374
375            // Keyboard handler
376            term.handle({
377                move |term, e| {
378                    match e {
379                        fltk::enums::Event::KeyDown
380                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
381                        {
382                            // false to let FLTK handle ESC. true to hide ESC
383                            false
384                        }
385
386                        fltk::enums::Event::KeyDown
387                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
388                        {
389                            // We handle control keystroke
390                            let k = fltk::app::event_text();
391                            term.append_utf8(&k);
392                            true
393                        }
394
395                        fltk::enums::Event::KeyDown
396                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
397                        {
398                            // We handle normal printable keystroke
399                            let k = fltk::app::event_text();
400                            term.take_focus().unwrap();
401                            term.append(&k);
402                            true
403                        }
404
405                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
406                        // We can do this, or else ignore them (return false)
407                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
408                        //     term.redraw();
409                        //     true
410                        // }
411                        _ => false, // Let FLTK handle everything else
412                    }
413                }
414            });
415
416            let attr_save = term.text_attrib();
417            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
418            term.append("\nStartup tests complete. Keyboard is live.\n");
419            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
420            term.set_text_attrib(attr_save);
421            assert_eq!(term.text_attrib(), attr_save);
422            term.redraw();
423        }
424    });
425
426    app.run().unwrap();
427}
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 353)
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!(term.text(true), "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");
115
116            // Testing ansi() and set_ansi() methods
117            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
118            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
119            term.set_ansi(false);
120            assert!(!term.ansi());
121            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
122            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
123            term.append_u8(b"Appending u8 array\n");
124            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
125            term.set_ansi(true); // Restore ANSI state
126
127            // Play with the horizontal scrollbar
128            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
129            term.set_hscrollbar_style(ScrollbarStyle::ON);
130            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
131
132            // Test show_unknown() as incidental part of testing append methods
133            term.set_show_unknown(true);
134            assert!(term.show_unknown());
135            term.append_ascii(
136                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
137            );
138            term.set_show_unknown(false);
139            assert!(!term.show_unknown());
140
141            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
142            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
143
144            let r = term.cursor_row();
145            assert_eq!(term.cursor_col(), 0);
146            term.append(&format!("Testing cursor row/col {r}"));
147            assert_eq!(term.cursor_col(), 24);
148            assert_eq!(term.cursor_row(), r);
149
150            // Test cursor color methods
151            assert_eq!(
152                term.cursor_bg_color(),
153                Color::XtermGreen,
154                "Default cursor bg at startup"
155            );
156            assert_eq!(
157                term.cursor_fg_color(),
158                Color::from_hex(0xff_ff_f0),
159                "Default cursor fg at startup"
160            );
161            term.set_cursor_bg_color(Color::Red);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
164            term.set_cursor_fg_color(Color::Blue);
165            assert_eq!(term.cursor_bg_color(), Color::Red);
166            assert_eq!(term.cursor_fg_color(), Color::Blue);
167            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
168            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
169            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
170            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
171
172            // The default display_rows() will derive from the window size
173            let dr = term.display_rows();
174            let height = term.height();
175            assert_eq!(height, term.h());
176            assert!(dr > 20, "Default display_rows at startup");
177            term.resize(term.x(), term.y(), term.w(), height * 2);
178            assert_eq!(term.h(), height * 2);
179            assert_eq!(height * 2, term.h());
180            assert!(term.display_rows() > dr);
181            term.resize(term.x(), term.y(), term.w(), height);
182
183            // The default display_columns() will derive from the window size
184            let dc = term.display_columns();
185            assert!(dc > 80, "Default display_rows at startup");
186            term.set_display_columns(200);
187            assert_eq!(term.display_columns(), 200);
188            term.append("\n         1         2         3         4         5         6         7         8         9");
189            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
190            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
191            term.set_display_columns(90);
192            assert_eq!(term.display_columns(), 90);
193            term.set_display_columns(dc); // Set back to default
194            assert_eq!(term.display_columns(), dc);
195
196            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
197            term.set_history_rows(50);
198            assert_eq!(term.history_rows(), 50);
199            term.set_history_rows(100); // Set back to default
200            assert_eq!(term.history_rows(), 100);
201
202            let hu = term.history_use();
203            term.append(&format!(
204                "history_use = {hu} (it's not clear what this means)\n"
205            ));
206            // assert_eq!(term.history_use(), hu+1);
207
208            term.append(&format!(
209                "margins = b:{} l:{} r:{} t{}\n",
210                term.margin_bottom(),
211                term.margin_left(),
212                term.margin_right(),
213                term.margin_top()
214            ));
215            assert_eq!(term.margin_bottom(), 3);
216            assert_eq!(term.margin_left(), 3);
217            assert_eq!(term.margin_right(), 3);
218            assert_eq!(term.margin_top(), 3);
219
220            term.set_margin_bottom(5);
221            term.set_margin_left(10);
222            term.set_margin_right(15);
223            term.set_margin_top(20);
224            assert_eq!(term.margin_bottom(), 5);
225            assert_eq!(term.margin_left(), 10);
226            assert_eq!(term.margin_right(), 15);
227            assert_eq!(term.margin_top(), 20);
228
229            term.append("Single character: '");
230            term.print_char('X');
231            term.append("', single UTF-8 character: '");
232            term.print_char_utf8('↑');
233            term.append("'\n");
234
235            let rr = term.redraw_rate();
236            assert_eq!(rr, 0.1, "Default redraw rate at startup");
237            term.append(&format!("Redraw rate {rr}\n"));
238            term.set_redraw_rate(1.0);
239            assert_eq!(term.redraw_rate(), 1.0);
240            term.set_redraw_rate(rr);
241            assert_eq!(term.redraw_rate(), rr);
242
243            let rs = term.redraw_style();
244            term.append(&format!("Redraw style {rs:?}\n"));
245            assert_eq!(
246                rs,
247                RedrawStyle::RateLimited,
248                "Default redraw style at startup"
249            );
250            term.set_redraw_style(RedrawStyle::NoRedraw);
251            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
252            term.set_redraw_style(rs);
253            assert_eq!(term.redraw_style(), rs);
254
255            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
256            assert_eq!(
257                RedrawStyle::NoRedraw.bits(),
258                0x0000,
259                "RedrawStyle enum values have been reassigned"
260            );
261            assert_eq!(
262                RedrawStyle::RateLimited.bits(),
263                0x0001,
264                "RedrawStyle enum values have been reassigned"
265            );
266            assert_eq!(
267                RedrawStyle::PerWrite.bits(),
268                0x0002,
269                "RedrawStyle enum values have been reassigned"
270            );
271
272            let sb = term.scrollbar();
273            let hsb = term.hscrollbar();
274            // Both vertical and horizontal scrollbars are at zero
275            assert_eq!(sb.value(), 0.0);
276            assert_eq!(hsb.value(), 0.0);
277            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
278
279            term.append(&format!(
280                "Scrollbar actual size {}\n",
281                term.scrollbar_actual_size()
282            ));
283            assert_eq!(term.scrollbar_actual_size(), 16);
284            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
285            assert_eq!(
286                term.scrollbar_size(),
287                0,
288                "Default scrollbar size at startup"
289            );
290            term.set_scrollbar_size(40);
291            assert_eq!(term.scrollbar_size(), 40);
292            assert_eq!(term.scrollbar_actual_size(), 40);
293            term.append(&format!(
294                "Scrollbar actual size {}\n",
295                term.scrollbar_actual_size()
296            ));
297            term.set_scrollbar_size(0); // Restore default
298            assert_eq!(term.scrollbar_size(), 0);
299            assert_eq!(term.scrollbar_actual_size(), 16);
300
301            let sfc = term.selection_fg_color();
302            let sbc = term.selection_bg_color();
303            assert_eq!(sfc, Color::Black);
304            assert_eq!(sbc, Color::White);
305            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
306            term.set_selection_fg_color(Color::Green);
307            term.set_selection_bg_color(Color::DarkBlue);
308            assert_eq!(term.selection_fg_color(), Color::Green);
309            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
310            term.set_selection_fg_color(sfc);
311            term.set_selection_bg_color(sbc);
312            assert_eq!(term.selection_fg_color(), Color::Black);
313            assert_eq!(term.selection_bg_color(), Color::White);
314
315            let tfcd = term.text_fg_color_default();
316            let tbcd = term.text_bg_color_default();
317            assert_eq!(tfcd, Color::XtermWhite);
318            assert_eq!(tbcd, Color::TransparentBg);
319            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
320            term.set_text_fg_color_default(Color::Green);
321            term.set_text_bg_color_default(Color::DarkBlue);
322            assert_eq!(term.text_fg_color_default(), Color::Green);
323            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
324            term.set_text_fg_color_default(tfcd);
325            term.set_text_bg_color_default(tbcd);
326            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
327            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
328
329            let tfc = term.text_fg_color();
330            let tbc = term.text_bg_color();
331            assert_eq!(tfc, Color::XtermWhite);
332            assert_eq!(tbc, Color::TransparentBg);
333            term.append(&format!("Text colors: {sfc} {sbc}\n"));
334            term.set_text_fg_color(Color::Green);
335            term.set_text_bg_color(Color::DarkBlue);
336            assert_eq!(term.text_fg_color(), Color::Green);
337            assert_eq!(term.text_bg_color(), Color::DarkBlue);
338            term.set_text_fg_color(tfc);
339            term.set_text_bg_color(tbc);
340            assert_eq!(term.text_fg_color(), Color::XtermWhite);
341            assert_eq!(term.text_bg_color(), Color::TransparentBg);
342
343            let tf = term.text_font();
344            term.append(&format!("Text font: {tf:?}\n"));
345            assert_eq!(tf, Font::Courier);
346            term.set_text_font(Font::Screen);
347            assert_eq!(term.text_font(), Font::Screen);
348            term.set_text_font(tf);
349            assert_eq!(term.text_font(), Font::Courier);
350
351            let ts = term.text_size();
352            let r = term.h_to_row(100);
353            let c = term.w_to_col(100);
354            term.append(&format!(
355                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
356            ));
357            assert_eq!(ts, 14);
358            term.set_text_size(30);
359            assert_eq!(term.text_size(), 30);
360            term.append(&format!(
361                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
362                term.text_size(),
363                term.h_to_row(100),
364                term.w_to_col(100)
365            ));
366            term.set_text_size(ts);
367            assert_eq!(term.text_size(), ts);
368            term.append(&format!(
369                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
370                term.text_size(),
371                term.h_to_row(100),
372                term.w_to_col(100)
373            ));
374
375            // Keyboard handler
376            term.handle({
377                move |term, e| {
378                    match e {
379                        fltk::enums::Event::KeyDown
380                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
381                        {
382                            // false to let FLTK handle ESC. true to hide ESC
383                            false
384                        }
385
386                        fltk::enums::Event::KeyDown
387                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
388                        {
389                            // We handle control keystroke
390                            let k = fltk::app::event_text();
391                            term.append_utf8(&k);
392                            true
393                        }
394
395                        fltk::enums::Event::KeyDown
396                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
397                        {
398                            // We handle normal printable keystroke
399                            let k = fltk::app::event_text();
400                            term.take_focus().unwrap();
401                            term.append(&k);
402                            true
403                        }
404
405                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
406                        // We can do this, or else ignore them (return false)
407                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
408                        //     term.redraw();
409                        //     true
410                        // }
411                        _ => false, // Let FLTK handle everything else
412                    }
413                }
414            });
415
416            let attr_save = term.text_attrib();
417            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
418            term.append("\nStartup tests complete. Keyboard is live.\n");
419            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
420            term.set_text_attrib(attr_save);
421            assert_eq!(term.text_attrib(), attr_save);
422            term.redraw();
423        }
424    });
425
426    app.run().unwrap();
427}
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 544)
498fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
499    term.take_focus().unwrap();
500    term.reset_terminal();
501    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
502
503    assert_eq!(term.history_use(), 0);
504    term.clear();
505    assert_eq!(term.cursor_row(), 0);
506    assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
507
508    term.append("Test\ntext\na\nb\nc\nd");
509    assert_eq!(term.cursor_row(), 5);
510    let hist = term.history_use();
511    term.clear_screen_home(false);
512    assert_eq!(term.cursor_row(), 0);
513    assert_eq!(term.history_use(), hist); // History not changed
514
515    term.append("Test\ntext\na\nb\nc\nd\ne");
516    assert_eq!(term.cursor_row(), 6);
517    term.clear_screen_home(true);
518    assert_eq!(term.cursor_row(), 0);
519
520    term.append("Test\ntext\na\nb\nc\n");
521    assert_eq!(term.cursor_row(), 5);
522    term.clear_to_color(Color::DarkBlue);
523    assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
524    assert_eq!(term.text_bg_color(), Color::TransparentBg);
525    assert_eq!(term.cursor_row(), 0);
526
527    // Test cursor_home()
528    term.append("Test\n\n\n\n\n\n\n\n\n\n");
529    assert_eq!(term.cursor_row(), 10);
530    term.cursor_home();
531    assert_eq!(term.cursor_row(), 0);
532
533    // Test the widget color
534    assert_eq!(term.color(), Color::Black); // Default
535    term.set_color(Color::DarkGreen);
536    assert_eq!(term.color(), Color::DarkGreen);
537    term.set_color(Color::Black);
538    assert_eq!(term.color(), Color::Black);
539    term.append(
540        "This should be one line of white text on black, embedded into the top of a blue field.\n",
541    );
542
543    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
544    term.set_output_translate(OutFlags::OFF);
545    assert_eq!(term.output_translate(), OutFlags::OFF);
546    term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
547    assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
548
549    term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
550    term.append("\nDone!\n");
551    term.set_text_attrib(Attrib::Normal);
552}
Source

pub fn output_translate(&self) -> OutFlags

Return the current combined output translation flags.

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

pub fn redraw_rate(&self) -> f32

Get max rate redraw speed in floating point seconds.

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!(term.text(true), "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");
115
116            // Testing ansi() and set_ansi() methods
117            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
118            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
119            term.set_ansi(false);
120            assert!(!term.ansi());
121            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
122            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
123            term.append_u8(b"Appending u8 array\n");
124            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
125            term.set_ansi(true); // Restore ANSI state
126
127            // Play with the horizontal scrollbar
128            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
129            term.set_hscrollbar_style(ScrollbarStyle::ON);
130            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
131
132            // Test show_unknown() as incidental part of testing append methods
133            term.set_show_unknown(true);
134            assert!(term.show_unknown());
135            term.append_ascii(
136                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
137            );
138            term.set_show_unknown(false);
139            assert!(!term.show_unknown());
140
141            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
142            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
143
144            let r = term.cursor_row();
145            assert_eq!(term.cursor_col(), 0);
146            term.append(&format!("Testing cursor row/col {r}"));
147            assert_eq!(term.cursor_col(), 24);
148            assert_eq!(term.cursor_row(), r);
149
150            // Test cursor color methods
151            assert_eq!(
152                term.cursor_bg_color(),
153                Color::XtermGreen,
154                "Default cursor bg at startup"
155            );
156            assert_eq!(
157                term.cursor_fg_color(),
158                Color::from_hex(0xff_ff_f0),
159                "Default cursor fg at startup"
160            );
161            term.set_cursor_bg_color(Color::Red);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
164            term.set_cursor_fg_color(Color::Blue);
165            assert_eq!(term.cursor_bg_color(), Color::Red);
166            assert_eq!(term.cursor_fg_color(), Color::Blue);
167            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
168            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
169            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
170            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
171
172            // The default display_rows() will derive from the window size
173            let dr = term.display_rows();
174            let height = term.height();
175            assert_eq!(height, term.h());
176            assert!(dr > 20, "Default display_rows at startup");
177            term.resize(term.x(), term.y(), term.w(), height * 2);
178            assert_eq!(term.h(), height * 2);
179            assert_eq!(height * 2, term.h());
180            assert!(term.display_rows() > dr);
181            term.resize(term.x(), term.y(), term.w(), height);
182
183            // The default display_columns() will derive from the window size
184            let dc = term.display_columns();
185            assert!(dc > 80, "Default display_rows at startup");
186            term.set_display_columns(200);
187            assert_eq!(term.display_columns(), 200);
188            term.append("\n         1         2         3         4         5         6         7         8         9");
189            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
190            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
191            term.set_display_columns(90);
192            assert_eq!(term.display_columns(), 90);
193            term.set_display_columns(dc); // Set back to default
194            assert_eq!(term.display_columns(), dc);
195
196            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
197            term.set_history_rows(50);
198            assert_eq!(term.history_rows(), 50);
199            term.set_history_rows(100); // Set back to default
200            assert_eq!(term.history_rows(), 100);
201
202            let hu = term.history_use();
203            term.append(&format!(
204                "history_use = {hu} (it's not clear what this means)\n"
205            ));
206            // assert_eq!(term.history_use(), hu+1);
207
208            term.append(&format!(
209                "margins = b:{} l:{} r:{} t{}\n",
210                term.margin_bottom(),
211                term.margin_left(),
212                term.margin_right(),
213                term.margin_top()
214            ));
215            assert_eq!(term.margin_bottom(), 3);
216            assert_eq!(term.margin_left(), 3);
217            assert_eq!(term.margin_right(), 3);
218            assert_eq!(term.margin_top(), 3);
219
220            term.set_margin_bottom(5);
221            term.set_margin_left(10);
222            term.set_margin_right(15);
223            term.set_margin_top(20);
224            assert_eq!(term.margin_bottom(), 5);
225            assert_eq!(term.margin_left(), 10);
226            assert_eq!(term.margin_right(), 15);
227            assert_eq!(term.margin_top(), 20);
228
229            term.append("Single character: '");
230            term.print_char('X');
231            term.append("', single UTF-8 character: '");
232            term.print_char_utf8('↑');
233            term.append("'\n");
234
235            let rr = term.redraw_rate();
236            assert_eq!(rr, 0.1, "Default redraw rate at startup");
237            term.append(&format!("Redraw rate {rr}\n"));
238            term.set_redraw_rate(1.0);
239            assert_eq!(term.redraw_rate(), 1.0);
240            term.set_redraw_rate(rr);
241            assert_eq!(term.redraw_rate(), rr);
242
243            let rs = term.redraw_style();
244            term.append(&format!("Redraw style {rs:?}\n"));
245            assert_eq!(
246                rs,
247                RedrawStyle::RateLimited,
248                "Default redraw style at startup"
249            );
250            term.set_redraw_style(RedrawStyle::NoRedraw);
251            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
252            term.set_redraw_style(rs);
253            assert_eq!(term.redraw_style(), rs);
254
255            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
256            assert_eq!(
257                RedrawStyle::NoRedraw.bits(),
258                0x0000,
259                "RedrawStyle enum values have been reassigned"
260            );
261            assert_eq!(
262                RedrawStyle::RateLimited.bits(),
263                0x0001,
264                "RedrawStyle enum values have been reassigned"
265            );
266            assert_eq!(
267                RedrawStyle::PerWrite.bits(),
268                0x0002,
269                "RedrawStyle enum values have been reassigned"
270            );
271
272            let sb = term.scrollbar();
273            let hsb = term.hscrollbar();
274            // Both vertical and horizontal scrollbars are at zero
275            assert_eq!(sb.value(), 0.0);
276            assert_eq!(hsb.value(), 0.0);
277            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
278
279            term.append(&format!(
280                "Scrollbar actual size {}\n",
281                term.scrollbar_actual_size()
282            ));
283            assert_eq!(term.scrollbar_actual_size(), 16);
284            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
285            assert_eq!(
286                term.scrollbar_size(),
287                0,
288                "Default scrollbar size at startup"
289            );
290            term.set_scrollbar_size(40);
291            assert_eq!(term.scrollbar_size(), 40);
292            assert_eq!(term.scrollbar_actual_size(), 40);
293            term.append(&format!(
294                "Scrollbar actual size {}\n",
295                term.scrollbar_actual_size()
296            ));
297            term.set_scrollbar_size(0); // Restore default
298            assert_eq!(term.scrollbar_size(), 0);
299            assert_eq!(term.scrollbar_actual_size(), 16);
300
301            let sfc = term.selection_fg_color();
302            let sbc = term.selection_bg_color();
303            assert_eq!(sfc, Color::Black);
304            assert_eq!(sbc, Color::White);
305            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
306            term.set_selection_fg_color(Color::Green);
307            term.set_selection_bg_color(Color::DarkBlue);
308            assert_eq!(term.selection_fg_color(), Color::Green);
309            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
310            term.set_selection_fg_color(sfc);
311            term.set_selection_bg_color(sbc);
312            assert_eq!(term.selection_fg_color(), Color::Black);
313            assert_eq!(term.selection_bg_color(), Color::White);
314
315            let tfcd = term.text_fg_color_default();
316            let tbcd = term.text_bg_color_default();
317            assert_eq!(tfcd, Color::XtermWhite);
318            assert_eq!(tbcd, Color::TransparentBg);
319            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
320            term.set_text_fg_color_default(Color::Green);
321            term.set_text_bg_color_default(Color::DarkBlue);
322            assert_eq!(term.text_fg_color_default(), Color::Green);
323            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
324            term.set_text_fg_color_default(tfcd);
325            term.set_text_bg_color_default(tbcd);
326            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
327            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
328
329            let tfc = term.text_fg_color();
330            let tbc = term.text_bg_color();
331            assert_eq!(tfc, Color::XtermWhite);
332            assert_eq!(tbc, Color::TransparentBg);
333            term.append(&format!("Text colors: {sfc} {sbc}\n"));
334            term.set_text_fg_color(Color::Green);
335            term.set_text_bg_color(Color::DarkBlue);
336            assert_eq!(term.text_fg_color(), Color::Green);
337            assert_eq!(term.text_bg_color(), Color::DarkBlue);
338            term.set_text_fg_color(tfc);
339            term.set_text_bg_color(tbc);
340            assert_eq!(term.text_fg_color(), Color::XtermWhite);
341            assert_eq!(term.text_bg_color(), Color::TransparentBg);
342
343            let tf = term.text_font();
344            term.append(&format!("Text font: {tf:?}\n"));
345            assert_eq!(tf, Font::Courier);
346            term.set_text_font(Font::Screen);
347            assert_eq!(term.text_font(), Font::Screen);
348            term.set_text_font(tf);
349            assert_eq!(term.text_font(), Font::Courier);
350
351            let ts = term.text_size();
352            let r = term.h_to_row(100);
353            let c = term.w_to_col(100);
354            term.append(&format!(
355                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
356            ));
357            assert_eq!(ts, 14);
358            term.set_text_size(30);
359            assert_eq!(term.text_size(), 30);
360            term.append(&format!(
361                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
362                term.text_size(),
363                term.h_to_row(100),
364                term.w_to_col(100)
365            ));
366            term.set_text_size(ts);
367            assert_eq!(term.text_size(), ts);
368            term.append(&format!(
369                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
370                term.text_size(),
371                term.h_to_row(100),
372                term.w_to_col(100)
373            ));
374
375            // Keyboard handler
376            term.handle({
377                move |term, e| {
378                    match e {
379                        fltk::enums::Event::KeyDown
380                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
381                        {
382                            // false to let FLTK handle ESC. true to hide ESC
383                            false
384                        }
385
386                        fltk::enums::Event::KeyDown
387                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
388                        {
389                            // We handle control keystroke
390                            let k = fltk::app::event_text();
391                            term.append_utf8(&k);
392                            true
393                        }
394
395                        fltk::enums::Event::KeyDown
396                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
397                        {
398                            // We handle normal printable keystroke
399                            let k = fltk::app::event_text();
400                            term.take_focus().unwrap();
401                            term.append(&k);
402                            true
403                        }
404
405                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
406                        // We can do this, or else ignore them (return false)
407                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
408                        //     term.redraw();
409                        //     true
410                        // }
411                        _ => false, // Let FLTK handle everything else
412                    }
413                }
414            });
415
416            let attr_save = term.text_attrib();
417            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
418            term.append("\nStartup tests complete. Keyboard is live.\n");
419            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
420            term.set_text_attrib(attr_save);
421            assert_eq!(term.text_attrib(), attr_save);
422            term.redraw();
423        }
424    });
425
426    app.run().unwrap();
427}
Source

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

Set how Terminal manages screen redrawing.

Examples found in repository?
examples/terminal.rs (line 250)
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!(term.text(true), "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");
115
116            // Testing ansi() and set_ansi() methods
117            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
118            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
119            term.set_ansi(false);
120            assert!(!term.ansi());
121            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
122            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
123            term.append_u8(b"Appending u8 array\n");
124            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
125            term.set_ansi(true); // Restore ANSI state
126
127            // Play with the horizontal scrollbar
128            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
129            term.set_hscrollbar_style(ScrollbarStyle::ON);
130            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
131
132            // Test show_unknown() as incidental part of testing append methods
133            term.set_show_unknown(true);
134            assert!(term.show_unknown());
135            term.append_ascii(
136                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
137            );
138            term.set_show_unknown(false);
139            assert!(!term.show_unknown());
140
141            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
142            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
143
144            let r = term.cursor_row();
145            assert_eq!(term.cursor_col(), 0);
146            term.append(&format!("Testing cursor row/col {r}"));
147            assert_eq!(term.cursor_col(), 24);
148            assert_eq!(term.cursor_row(), r);
149
150            // Test cursor color methods
151            assert_eq!(
152                term.cursor_bg_color(),
153                Color::XtermGreen,
154                "Default cursor bg at startup"
155            );
156            assert_eq!(
157                term.cursor_fg_color(),
158                Color::from_hex(0xff_ff_f0),
159                "Default cursor fg at startup"
160            );
161            term.set_cursor_bg_color(Color::Red);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
164            term.set_cursor_fg_color(Color::Blue);
165            assert_eq!(term.cursor_bg_color(), Color::Red);
166            assert_eq!(term.cursor_fg_color(), Color::Blue);
167            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
168            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
169            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
170            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
171
172            // The default display_rows() will derive from the window size
173            let dr = term.display_rows();
174            let height = term.height();
175            assert_eq!(height, term.h());
176            assert!(dr > 20, "Default display_rows at startup");
177            term.resize(term.x(), term.y(), term.w(), height * 2);
178            assert_eq!(term.h(), height * 2);
179            assert_eq!(height * 2, term.h());
180            assert!(term.display_rows() > dr);
181            term.resize(term.x(), term.y(), term.w(), height);
182
183            // The default display_columns() will derive from the window size
184            let dc = term.display_columns();
185            assert!(dc > 80, "Default display_rows at startup");
186            term.set_display_columns(200);
187            assert_eq!(term.display_columns(), 200);
188            term.append("\n         1         2         3         4         5         6         7         8         9");
189            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
190            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
191            term.set_display_columns(90);
192            assert_eq!(term.display_columns(), 90);
193            term.set_display_columns(dc); // Set back to default
194            assert_eq!(term.display_columns(), dc);
195
196            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
197            term.set_history_rows(50);
198            assert_eq!(term.history_rows(), 50);
199            term.set_history_rows(100); // Set back to default
200            assert_eq!(term.history_rows(), 100);
201
202            let hu = term.history_use();
203            term.append(&format!(
204                "history_use = {hu} (it's not clear what this means)\n"
205            ));
206            // assert_eq!(term.history_use(), hu+1);
207
208            term.append(&format!(
209                "margins = b:{} l:{} r:{} t{}\n",
210                term.margin_bottom(),
211                term.margin_left(),
212                term.margin_right(),
213                term.margin_top()
214            ));
215            assert_eq!(term.margin_bottom(), 3);
216            assert_eq!(term.margin_left(), 3);
217            assert_eq!(term.margin_right(), 3);
218            assert_eq!(term.margin_top(), 3);
219
220            term.set_margin_bottom(5);
221            term.set_margin_left(10);
222            term.set_margin_right(15);
223            term.set_margin_top(20);
224            assert_eq!(term.margin_bottom(), 5);
225            assert_eq!(term.margin_left(), 10);
226            assert_eq!(term.margin_right(), 15);
227            assert_eq!(term.margin_top(), 20);
228
229            term.append("Single character: '");
230            term.print_char('X');
231            term.append("', single UTF-8 character: '");
232            term.print_char_utf8('↑');
233            term.append("'\n");
234
235            let rr = term.redraw_rate();
236            assert_eq!(rr, 0.1, "Default redraw rate at startup");
237            term.append(&format!("Redraw rate {rr}\n"));
238            term.set_redraw_rate(1.0);
239            assert_eq!(term.redraw_rate(), 1.0);
240            term.set_redraw_rate(rr);
241            assert_eq!(term.redraw_rate(), rr);
242
243            let rs = term.redraw_style();
244            term.append(&format!("Redraw style {rs:?}\n"));
245            assert_eq!(
246                rs,
247                RedrawStyle::RateLimited,
248                "Default redraw style at startup"
249            );
250            term.set_redraw_style(RedrawStyle::NoRedraw);
251            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
252            term.set_redraw_style(rs);
253            assert_eq!(term.redraw_style(), rs);
254
255            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
256            assert_eq!(
257                RedrawStyle::NoRedraw.bits(),
258                0x0000,
259                "RedrawStyle enum values have been reassigned"
260            );
261            assert_eq!(
262                RedrawStyle::RateLimited.bits(),
263                0x0001,
264                "RedrawStyle enum values have been reassigned"
265            );
266            assert_eq!(
267                RedrawStyle::PerWrite.bits(),
268                0x0002,
269                "RedrawStyle enum values have been reassigned"
270            );
271
272            let sb = term.scrollbar();
273            let hsb = term.hscrollbar();
274            // Both vertical and horizontal scrollbars are at zero
275            assert_eq!(sb.value(), 0.0);
276            assert_eq!(hsb.value(), 0.0);
277            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
278
279            term.append(&format!(
280                "Scrollbar actual size {}\n",
281                term.scrollbar_actual_size()
282            ));
283            assert_eq!(term.scrollbar_actual_size(), 16);
284            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
285            assert_eq!(
286                term.scrollbar_size(),
287                0,
288                "Default scrollbar size at startup"
289            );
290            term.set_scrollbar_size(40);
291            assert_eq!(term.scrollbar_size(), 40);
292            assert_eq!(term.scrollbar_actual_size(), 40);
293            term.append(&format!(
294                "Scrollbar actual size {}\n",
295                term.scrollbar_actual_size()
296            ));
297            term.set_scrollbar_size(0); // Restore default
298            assert_eq!(term.scrollbar_size(), 0);
299            assert_eq!(term.scrollbar_actual_size(), 16);
300
301            let sfc = term.selection_fg_color();
302            let sbc = term.selection_bg_color();
303            assert_eq!(sfc, Color::Black);
304            assert_eq!(sbc, Color::White);
305            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
306            term.set_selection_fg_color(Color::Green);
307            term.set_selection_bg_color(Color::DarkBlue);
308            assert_eq!(term.selection_fg_color(), Color::Green);
309            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
310            term.set_selection_fg_color(sfc);
311            term.set_selection_bg_color(sbc);
312            assert_eq!(term.selection_fg_color(), Color::Black);
313            assert_eq!(term.selection_bg_color(), Color::White);
314
315            let tfcd = term.text_fg_color_default();
316            let tbcd = term.text_bg_color_default();
317            assert_eq!(tfcd, Color::XtermWhite);
318            assert_eq!(tbcd, Color::TransparentBg);
319            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
320            term.set_text_fg_color_default(Color::Green);
321            term.set_text_bg_color_default(Color::DarkBlue);
322            assert_eq!(term.text_fg_color_default(), Color::Green);
323            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
324            term.set_text_fg_color_default(tfcd);
325            term.set_text_bg_color_default(tbcd);
326            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
327            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
328
329            let tfc = term.text_fg_color();
330            let tbc = term.text_bg_color();
331            assert_eq!(tfc, Color::XtermWhite);
332            assert_eq!(tbc, Color::TransparentBg);
333            term.append(&format!("Text colors: {sfc} {sbc}\n"));
334            term.set_text_fg_color(Color::Green);
335            term.set_text_bg_color(Color::DarkBlue);
336            assert_eq!(term.text_fg_color(), Color::Green);
337            assert_eq!(term.text_bg_color(), Color::DarkBlue);
338            term.set_text_fg_color(tfc);
339            term.set_text_bg_color(tbc);
340            assert_eq!(term.text_fg_color(), Color::XtermWhite);
341            assert_eq!(term.text_bg_color(), Color::TransparentBg);
342
343            let tf = term.text_font();
344            term.append(&format!("Text font: {tf:?}\n"));
345            assert_eq!(tf, Font::Courier);
346            term.set_text_font(Font::Screen);
347            assert_eq!(term.text_font(), Font::Screen);
348            term.set_text_font(tf);
349            assert_eq!(term.text_font(), Font::Courier);
350
351            let ts = term.text_size();
352            let r = term.h_to_row(100);
353            let c = term.w_to_col(100);
354            term.append(&format!(
355                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
356            ));
357            assert_eq!(ts, 14);
358            term.set_text_size(30);
359            assert_eq!(term.text_size(), 30);
360            term.append(&format!(
361                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
362                term.text_size(),
363                term.h_to_row(100),
364                term.w_to_col(100)
365            ));
366            term.set_text_size(ts);
367            assert_eq!(term.text_size(), ts);
368            term.append(&format!(
369                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
370                term.text_size(),
371                term.h_to_row(100),
372                term.w_to_col(100)
373            ));
374
375            // Keyboard handler
376            term.handle({
377                move |term, e| {
378                    match e {
379                        fltk::enums::Event::KeyDown
380                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
381                        {
382                            // false to let FLTK handle ESC. true to hide ESC
383                            false
384                        }
385
386                        fltk::enums::Event::KeyDown
387                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
388                        {
389                            // We handle control keystroke
390                            let k = fltk::app::event_text();
391                            term.append_utf8(&k);
392                            true
393                        }
394
395                        fltk::enums::Event::KeyDown
396                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
397                        {
398                            // We handle normal printable keystroke
399                            let k = fltk::app::event_text();
400                            term.take_focus().unwrap();
401                            term.append(&k);
402                            true
403                        }
404
405                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
406                        // We can do this, or else ignore them (return false)
407                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
408                        //     term.redraw();
409                        //     true
410                        // }
411                        _ => false, // Let FLTK handle everything else
412                    }
413                }
414            });
415
416            let attr_save = term.text_attrib();
417            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
418            term.append("\nStartup tests complete. Keyboard is live.\n");
419            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
420            term.set_text_attrib(attr_save);
421            assert_eq!(term.text_attrib(), attr_save);
422            term.redraw();
423        }
424    });
425
426    app.run().unwrap();
427}
Source

pub fn redraw_style(&self) -> RedrawStyle

Get the redraw style.

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

pub fn selection_bg_color(&self) -> Color

Get mouse selection background color.

Examples found in repository?
examples/terminal.rs (line 302)
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!(term.text(true), "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");
115
116            // Testing ansi() and set_ansi() methods
117            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
118            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
119            term.set_ansi(false);
120            assert!(!term.ansi());
121            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
122            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
123            term.append_u8(b"Appending u8 array\n");
124            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
125            term.set_ansi(true); // Restore ANSI state
126
127            // Play with the horizontal scrollbar
128            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
129            term.set_hscrollbar_style(ScrollbarStyle::ON);
130            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
131
132            // Test show_unknown() as incidental part of testing append methods
133            term.set_show_unknown(true);
134            assert!(term.show_unknown());
135            term.append_ascii(
136                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
137            );
138            term.set_show_unknown(false);
139            assert!(!term.show_unknown());
140
141            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
142            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
143
144            let r = term.cursor_row();
145            assert_eq!(term.cursor_col(), 0);
146            term.append(&format!("Testing cursor row/col {r}"));
147            assert_eq!(term.cursor_col(), 24);
148            assert_eq!(term.cursor_row(), r);
149
150            // Test cursor color methods
151            assert_eq!(
152                term.cursor_bg_color(),
153                Color::XtermGreen,
154                "Default cursor bg at startup"
155            );
156            assert_eq!(
157                term.cursor_fg_color(),
158                Color::from_hex(0xff_ff_f0),
159                "Default cursor fg at startup"
160            );
161            term.set_cursor_bg_color(Color::Red);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
164            term.set_cursor_fg_color(Color::Blue);
165            assert_eq!(term.cursor_bg_color(), Color::Red);
166            assert_eq!(term.cursor_fg_color(), Color::Blue);
167            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
168            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
169            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
170            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
171
172            // The default display_rows() will derive from the window size
173            let dr = term.display_rows();
174            let height = term.height();
175            assert_eq!(height, term.h());
176            assert!(dr > 20, "Default display_rows at startup");
177            term.resize(term.x(), term.y(), term.w(), height * 2);
178            assert_eq!(term.h(), height * 2);
179            assert_eq!(height * 2, term.h());
180            assert!(term.display_rows() > dr);
181            term.resize(term.x(), term.y(), term.w(), height);
182
183            // The default display_columns() will derive from the window size
184            let dc = term.display_columns();
185            assert!(dc > 80, "Default display_rows at startup");
186            term.set_display_columns(200);
187            assert_eq!(term.display_columns(), 200);
188            term.append("\n         1         2         3         4         5         6         7         8         9");
189            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
190            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
191            term.set_display_columns(90);
192            assert_eq!(term.display_columns(), 90);
193            term.set_display_columns(dc); // Set back to default
194            assert_eq!(term.display_columns(), dc);
195
196            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
197            term.set_history_rows(50);
198            assert_eq!(term.history_rows(), 50);
199            term.set_history_rows(100); // Set back to default
200            assert_eq!(term.history_rows(), 100);
201
202            let hu = term.history_use();
203            term.append(&format!(
204                "history_use = {hu} (it's not clear what this means)\n"
205            ));
206            // assert_eq!(term.history_use(), hu+1);
207
208            term.append(&format!(
209                "margins = b:{} l:{} r:{} t{}\n",
210                term.margin_bottom(),
211                term.margin_left(),
212                term.margin_right(),
213                term.margin_top()
214            ));
215            assert_eq!(term.margin_bottom(), 3);
216            assert_eq!(term.margin_left(), 3);
217            assert_eq!(term.margin_right(), 3);
218            assert_eq!(term.margin_top(), 3);
219
220            term.set_margin_bottom(5);
221            term.set_margin_left(10);
222            term.set_margin_right(15);
223            term.set_margin_top(20);
224            assert_eq!(term.margin_bottom(), 5);
225            assert_eq!(term.margin_left(), 10);
226            assert_eq!(term.margin_right(), 15);
227            assert_eq!(term.margin_top(), 20);
228
229            term.append("Single character: '");
230            term.print_char('X');
231            term.append("', single UTF-8 character: '");
232            term.print_char_utf8('↑');
233            term.append("'\n");
234
235            let rr = term.redraw_rate();
236            assert_eq!(rr, 0.1, "Default redraw rate at startup");
237            term.append(&format!("Redraw rate {rr}\n"));
238            term.set_redraw_rate(1.0);
239            assert_eq!(term.redraw_rate(), 1.0);
240            term.set_redraw_rate(rr);
241            assert_eq!(term.redraw_rate(), rr);
242
243            let rs = term.redraw_style();
244            term.append(&format!("Redraw style {rs:?}\n"));
245            assert_eq!(
246                rs,
247                RedrawStyle::RateLimited,
248                "Default redraw style at startup"
249            );
250            term.set_redraw_style(RedrawStyle::NoRedraw);
251            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
252            term.set_redraw_style(rs);
253            assert_eq!(term.redraw_style(), rs);
254
255            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
256            assert_eq!(
257                RedrawStyle::NoRedraw.bits(),
258                0x0000,
259                "RedrawStyle enum values have been reassigned"
260            );
261            assert_eq!(
262                RedrawStyle::RateLimited.bits(),
263                0x0001,
264                "RedrawStyle enum values have been reassigned"
265            );
266            assert_eq!(
267                RedrawStyle::PerWrite.bits(),
268                0x0002,
269                "RedrawStyle enum values have been reassigned"
270            );
271
272            let sb = term.scrollbar();
273            let hsb = term.hscrollbar();
274            // Both vertical and horizontal scrollbars are at zero
275            assert_eq!(sb.value(), 0.0);
276            assert_eq!(hsb.value(), 0.0);
277            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
278
279            term.append(&format!(
280                "Scrollbar actual size {}\n",
281                term.scrollbar_actual_size()
282            ));
283            assert_eq!(term.scrollbar_actual_size(), 16);
284            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
285            assert_eq!(
286                term.scrollbar_size(),
287                0,
288                "Default scrollbar size at startup"
289            );
290            term.set_scrollbar_size(40);
291            assert_eq!(term.scrollbar_size(), 40);
292            assert_eq!(term.scrollbar_actual_size(), 40);
293            term.append(&format!(
294                "Scrollbar actual size {}\n",
295                term.scrollbar_actual_size()
296            ));
297            term.set_scrollbar_size(0); // Restore default
298            assert_eq!(term.scrollbar_size(), 0);
299            assert_eq!(term.scrollbar_actual_size(), 16);
300
301            let sfc = term.selection_fg_color();
302            let sbc = term.selection_bg_color();
303            assert_eq!(sfc, Color::Black);
304            assert_eq!(sbc, Color::White);
305            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
306            term.set_selection_fg_color(Color::Green);
307            term.set_selection_bg_color(Color::DarkBlue);
308            assert_eq!(term.selection_fg_color(), Color::Green);
309            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
310            term.set_selection_fg_color(sfc);
311            term.set_selection_bg_color(sbc);
312            assert_eq!(term.selection_fg_color(), Color::Black);
313            assert_eq!(term.selection_bg_color(), Color::White);
314
315            let tfcd = term.text_fg_color_default();
316            let tbcd = term.text_bg_color_default();
317            assert_eq!(tfcd, Color::XtermWhite);
318            assert_eq!(tbcd, Color::TransparentBg);
319            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
320            term.set_text_fg_color_default(Color::Green);
321            term.set_text_bg_color_default(Color::DarkBlue);
322            assert_eq!(term.text_fg_color_default(), Color::Green);
323            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
324            term.set_text_fg_color_default(tfcd);
325            term.set_text_bg_color_default(tbcd);
326            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
327            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
328
329            let tfc = term.text_fg_color();
330            let tbc = term.text_bg_color();
331            assert_eq!(tfc, Color::XtermWhite);
332            assert_eq!(tbc, Color::TransparentBg);
333            term.append(&format!("Text colors: {sfc} {sbc}\n"));
334            term.set_text_fg_color(Color::Green);
335            term.set_text_bg_color(Color::DarkBlue);
336            assert_eq!(term.text_fg_color(), Color::Green);
337            assert_eq!(term.text_bg_color(), Color::DarkBlue);
338            term.set_text_fg_color(tfc);
339            term.set_text_bg_color(tbc);
340            assert_eq!(term.text_fg_color(), Color::XtermWhite);
341            assert_eq!(term.text_bg_color(), Color::TransparentBg);
342
343            let tf = term.text_font();
344            term.append(&format!("Text font: {tf:?}\n"));
345            assert_eq!(tf, Font::Courier);
346            term.set_text_font(Font::Screen);
347            assert_eq!(term.text_font(), Font::Screen);
348            term.set_text_font(tf);
349            assert_eq!(term.text_font(), Font::Courier);
350
351            let ts = term.text_size();
352            let r = term.h_to_row(100);
353            let c = term.w_to_col(100);
354            term.append(&format!(
355                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
356            ));
357            assert_eq!(ts, 14);
358            term.set_text_size(30);
359            assert_eq!(term.text_size(), 30);
360            term.append(&format!(
361                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
362                term.text_size(),
363                term.h_to_row(100),
364                term.w_to_col(100)
365            ));
366            term.set_text_size(ts);
367            assert_eq!(term.text_size(), ts);
368            term.append(&format!(
369                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
370                term.text_size(),
371                term.h_to_row(100),
372                term.w_to_col(100)
373            ));
374
375            // Keyboard handler
376            term.handle({
377                move |term, e| {
378                    match e {
379                        fltk::enums::Event::KeyDown
380                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
381                        {
382                            // false to let FLTK handle ESC. true to hide ESC
383                            false
384                        }
385
386                        fltk::enums::Event::KeyDown
387                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
388                        {
389                            // We handle control keystroke
390                            let k = fltk::app::event_text();
391                            term.append_utf8(&k);
392                            true
393                        }
394
395                        fltk::enums::Event::KeyDown
396                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
397                        {
398                            // We handle normal printable keystroke
399                            let k = fltk::app::event_text();
400                            term.take_focus().unwrap();
401                            term.append(&k);
402                            true
403                        }
404
405                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
406                        // We can do this, or else ignore them (return false)
407                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
408                        //     term.redraw();
409                        //     true
410                        // }
411                        _ => false, // Let FLTK handle everything else
412                    }
413                }
414            });
415
416            let attr_save = term.text_attrib();
417            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
418            term.append("\nStartup tests complete. Keyboard is live.\n");
419            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
420            term.set_text_attrib(attr_save);
421            assert_eq!(term.text_attrib(), attr_save);
422            term.redraw();
423        }
424    });
425
426    app.run().unwrap();
427}
Source

pub fn scrollbar(&self) -> Scrollbar

Returns the vertical scrollbar

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

pub fn hscrollbar(&self) -> Scrollbar

Returns the horizontal scrollbar

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

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

Set mouse selection background color.

Examples found in repository?
examples/terminal.rs (line 307)
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!(term.text(true), "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");
115
116            // Testing ansi() and set_ansi() methods
117            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
118            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
119            term.set_ansi(false);
120            assert!(!term.ansi());
121            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
122            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
123            term.append_u8(b"Appending u8 array\n");
124            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
125            term.set_ansi(true); // Restore ANSI state
126
127            // Play with the horizontal scrollbar
128            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
129            term.set_hscrollbar_style(ScrollbarStyle::ON);
130            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
131
132            // Test show_unknown() as incidental part of testing append methods
133            term.set_show_unknown(true);
134            assert!(term.show_unknown());
135            term.append_ascii(
136                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
137            );
138            term.set_show_unknown(false);
139            assert!(!term.show_unknown());
140
141            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
142            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
143
144            let r = term.cursor_row();
145            assert_eq!(term.cursor_col(), 0);
146            term.append(&format!("Testing cursor row/col {r}"));
147            assert_eq!(term.cursor_col(), 24);
148            assert_eq!(term.cursor_row(), r);
149
150            // Test cursor color methods
151            assert_eq!(
152                term.cursor_bg_color(),
153                Color::XtermGreen,
154                "Default cursor bg at startup"
155            );
156            assert_eq!(
157                term.cursor_fg_color(),
158                Color::from_hex(0xff_ff_f0),
159                "Default cursor fg at startup"
160            );
161            term.set_cursor_bg_color(Color::Red);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
164            term.set_cursor_fg_color(Color::Blue);
165            assert_eq!(term.cursor_bg_color(), Color::Red);
166            assert_eq!(term.cursor_fg_color(), Color::Blue);
167            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
168            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
169            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
170            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
171
172            // The default display_rows() will derive from the window size
173            let dr = term.display_rows();
174            let height = term.height();
175            assert_eq!(height, term.h());
176            assert!(dr > 20, "Default display_rows at startup");
177            term.resize(term.x(), term.y(), term.w(), height * 2);
178            assert_eq!(term.h(), height * 2);
179            assert_eq!(height * 2, term.h());
180            assert!(term.display_rows() > dr);
181            term.resize(term.x(), term.y(), term.w(), height);
182
183            // The default display_columns() will derive from the window size
184            let dc = term.display_columns();
185            assert!(dc > 80, "Default display_rows at startup");
186            term.set_display_columns(200);
187            assert_eq!(term.display_columns(), 200);
188            term.append("\n         1         2         3         4         5         6         7         8         9");
189            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
190            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
191            term.set_display_columns(90);
192            assert_eq!(term.display_columns(), 90);
193            term.set_display_columns(dc); // Set back to default
194            assert_eq!(term.display_columns(), dc);
195
196            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
197            term.set_history_rows(50);
198            assert_eq!(term.history_rows(), 50);
199            term.set_history_rows(100); // Set back to default
200            assert_eq!(term.history_rows(), 100);
201
202            let hu = term.history_use();
203            term.append(&format!(
204                "history_use = {hu} (it's not clear what this means)\n"
205            ));
206            // assert_eq!(term.history_use(), hu+1);
207
208            term.append(&format!(
209                "margins = b:{} l:{} r:{} t{}\n",
210                term.margin_bottom(),
211                term.margin_left(),
212                term.margin_right(),
213                term.margin_top()
214            ));
215            assert_eq!(term.margin_bottom(), 3);
216            assert_eq!(term.margin_left(), 3);
217            assert_eq!(term.margin_right(), 3);
218            assert_eq!(term.margin_top(), 3);
219
220            term.set_margin_bottom(5);
221            term.set_margin_left(10);
222            term.set_margin_right(15);
223            term.set_margin_top(20);
224            assert_eq!(term.margin_bottom(), 5);
225            assert_eq!(term.margin_left(), 10);
226            assert_eq!(term.margin_right(), 15);
227            assert_eq!(term.margin_top(), 20);
228
229            term.append("Single character: '");
230            term.print_char('X');
231            term.append("', single UTF-8 character: '");
232            term.print_char_utf8('↑');
233            term.append("'\n");
234
235            let rr = term.redraw_rate();
236            assert_eq!(rr, 0.1, "Default redraw rate at startup");
237            term.append(&format!("Redraw rate {rr}\n"));
238            term.set_redraw_rate(1.0);
239            assert_eq!(term.redraw_rate(), 1.0);
240            term.set_redraw_rate(rr);
241            assert_eq!(term.redraw_rate(), rr);
242
243            let rs = term.redraw_style();
244            term.append(&format!("Redraw style {rs:?}\n"));
245            assert_eq!(
246                rs,
247                RedrawStyle::RateLimited,
248                "Default redraw style at startup"
249            );
250            term.set_redraw_style(RedrawStyle::NoRedraw);
251            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
252            term.set_redraw_style(rs);
253            assert_eq!(term.redraw_style(), rs);
254
255            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
256            assert_eq!(
257                RedrawStyle::NoRedraw.bits(),
258                0x0000,
259                "RedrawStyle enum values have been reassigned"
260            );
261            assert_eq!(
262                RedrawStyle::RateLimited.bits(),
263                0x0001,
264                "RedrawStyle enum values have been reassigned"
265            );
266            assert_eq!(
267                RedrawStyle::PerWrite.bits(),
268                0x0002,
269                "RedrawStyle enum values have been reassigned"
270            );
271
272            let sb = term.scrollbar();
273            let hsb = term.hscrollbar();
274            // Both vertical and horizontal scrollbars are at zero
275            assert_eq!(sb.value(), 0.0);
276            assert_eq!(hsb.value(), 0.0);
277            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
278
279            term.append(&format!(
280                "Scrollbar actual size {}\n",
281                term.scrollbar_actual_size()
282            ));
283            assert_eq!(term.scrollbar_actual_size(), 16);
284            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
285            assert_eq!(
286                term.scrollbar_size(),
287                0,
288                "Default scrollbar size at startup"
289            );
290            term.set_scrollbar_size(40);
291            assert_eq!(term.scrollbar_size(), 40);
292            assert_eq!(term.scrollbar_actual_size(), 40);
293            term.append(&format!(
294                "Scrollbar actual size {}\n",
295                term.scrollbar_actual_size()
296            ));
297            term.set_scrollbar_size(0); // Restore default
298            assert_eq!(term.scrollbar_size(), 0);
299            assert_eq!(term.scrollbar_actual_size(), 16);
300
301            let sfc = term.selection_fg_color();
302            let sbc = term.selection_bg_color();
303            assert_eq!(sfc, Color::Black);
304            assert_eq!(sbc, Color::White);
305            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
306            term.set_selection_fg_color(Color::Green);
307            term.set_selection_bg_color(Color::DarkBlue);
308            assert_eq!(term.selection_fg_color(), Color::Green);
309            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
310            term.set_selection_fg_color(sfc);
311            term.set_selection_bg_color(sbc);
312            assert_eq!(term.selection_fg_color(), Color::Black);
313            assert_eq!(term.selection_bg_color(), Color::White);
314
315            let tfcd = term.text_fg_color_default();
316            let tbcd = term.text_bg_color_default();
317            assert_eq!(tfcd, Color::XtermWhite);
318            assert_eq!(tbcd, Color::TransparentBg);
319            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
320            term.set_text_fg_color_default(Color::Green);
321            term.set_text_bg_color_default(Color::DarkBlue);
322            assert_eq!(term.text_fg_color_default(), Color::Green);
323            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
324            term.set_text_fg_color_default(tfcd);
325            term.set_text_bg_color_default(tbcd);
326            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
327            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
328
329            let tfc = term.text_fg_color();
330            let tbc = term.text_bg_color();
331            assert_eq!(tfc, Color::XtermWhite);
332            assert_eq!(tbc, Color::TransparentBg);
333            term.append(&format!("Text colors: {sfc} {sbc}\n"));
334            term.set_text_fg_color(Color::Green);
335            term.set_text_bg_color(Color::DarkBlue);
336            assert_eq!(term.text_fg_color(), Color::Green);
337            assert_eq!(term.text_bg_color(), Color::DarkBlue);
338            term.set_text_fg_color(tfc);
339            term.set_text_bg_color(tbc);
340            assert_eq!(term.text_fg_color(), Color::XtermWhite);
341            assert_eq!(term.text_bg_color(), Color::TransparentBg);
342
343            let tf = term.text_font();
344            term.append(&format!("Text font: {tf:?}\n"));
345            assert_eq!(tf, Font::Courier);
346            term.set_text_font(Font::Screen);
347            assert_eq!(term.text_font(), Font::Screen);
348            term.set_text_font(tf);
349            assert_eq!(term.text_font(), Font::Courier);
350
351            let ts = term.text_size();
352            let r = term.h_to_row(100);
353            let c = term.w_to_col(100);
354            term.append(&format!(
355                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
356            ));
357            assert_eq!(ts, 14);
358            term.set_text_size(30);
359            assert_eq!(term.text_size(), 30);
360            term.append(&format!(
361                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
362                term.text_size(),
363                term.h_to_row(100),
364                term.w_to_col(100)
365            ));
366            term.set_text_size(ts);
367            assert_eq!(term.text_size(), ts);
368            term.append(&format!(
369                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
370                term.text_size(),
371                term.h_to_row(100),
372                term.w_to_col(100)
373            ));
374
375            // Keyboard handler
376            term.handle({
377                move |term, e| {
378                    match e {
379                        fltk::enums::Event::KeyDown
380                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
381                        {
382                            // false to let FLTK handle ESC. true to hide ESC
383                            false
384                        }
385
386                        fltk::enums::Event::KeyDown
387                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
388                        {
389                            // We handle control keystroke
390                            let k = fltk::app::event_text();
391                            term.append_utf8(&k);
392                            true
393                        }
394
395                        fltk::enums::Event::KeyDown
396                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
397                        {
398                            // We handle normal printable keystroke
399                            let k = fltk::app::event_text();
400                            term.take_focus().unwrap();
401                            term.append(&k);
402                            true
403                        }
404
405                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
406                        // We can do this, or else ignore them (return false)
407                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
408                        //     term.redraw();
409                        //     true
410                        // }
411                        _ => false, // Let FLTK handle everything else
412                    }
413                }
414            });
415
416            let attr_save = term.text_attrib();
417            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
418            term.append("\nStartup tests complete. Keyboard is live.\n");
419            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
420            term.set_text_attrib(attr_save);
421            assert_eq!(term.text_attrib(), attr_save);
422            term.redraw();
423        }
424    });
425
426    app.run().unwrap();
427}
Source

pub fn selection_fg_color(&self) -> Color

Get mouse selection foreground color.

Examples found in repository?
examples/terminal.rs (line 301)
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!(term.text(true), "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");
115
116            // Testing ansi() and set_ansi() methods
117            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
118            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
119            term.set_ansi(false);
120            assert!(!term.ansi());
121            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
122            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
123            term.append_u8(b"Appending u8 array\n");
124            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
125            term.set_ansi(true); // Restore ANSI state
126
127            // Play with the horizontal scrollbar
128            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
129            term.set_hscrollbar_style(ScrollbarStyle::ON);
130            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
131
132            // Test show_unknown() as incidental part of testing append methods
133            term.set_show_unknown(true);
134            assert!(term.show_unknown());
135            term.append_ascii(
136                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
137            );
138            term.set_show_unknown(false);
139            assert!(!term.show_unknown());
140
141            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
142            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
143
144            let r = term.cursor_row();
145            assert_eq!(term.cursor_col(), 0);
146            term.append(&format!("Testing cursor row/col {r}"));
147            assert_eq!(term.cursor_col(), 24);
148            assert_eq!(term.cursor_row(), r);
149
150            // Test cursor color methods
151            assert_eq!(
152                term.cursor_bg_color(),
153                Color::XtermGreen,
154                "Default cursor bg at startup"
155            );
156            assert_eq!(
157                term.cursor_fg_color(),
158                Color::from_hex(0xff_ff_f0),
159                "Default cursor fg at startup"
160            );
161            term.set_cursor_bg_color(Color::Red);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
164            term.set_cursor_fg_color(Color::Blue);
165            assert_eq!(term.cursor_bg_color(), Color::Red);
166            assert_eq!(term.cursor_fg_color(), Color::Blue);
167            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
168            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
169            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
170            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
171
172            // The default display_rows() will derive from the window size
173            let dr = term.display_rows();
174            let height = term.height();
175            assert_eq!(height, term.h());
176            assert!(dr > 20, "Default display_rows at startup");
177            term.resize(term.x(), term.y(), term.w(), height * 2);
178            assert_eq!(term.h(), height * 2);
179            assert_eq!(height * 2, term.h());
180            assert!(term.display_rows() > dr);
181            term.resize(term.x(), term.y(), term.w(), height);
182
183            // The default display_columns() will derive from the window size
184            let dc = term.display_columns();
185            assert!(dc > 80, "Default display_rows at startup");
186            term.set_display_columns(200);
187            assert_eq!(term.display_columns(), 200);
188            term.append("\n         1         2         3         4         5         6         7         8         9");
189            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
190            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
191            term.set_display_columns(90);
192            assert_eq!(term.display_columns(), 90);
193            term.set_display_columns(dc); // Set back to default
194            assert_eq!(term.display_columns(), dc);
195
196            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
197            term.set_history_rows(50);
198            assert_eq!(term.history_rows(), 50);
199            term.set_history_rows(100); // Set back to default
200            assert_eq!(term.history_rows(), 100);
201
202            let hu = term.history_use();
203            term.append(&format!(
204                "history_use = {hu} (it's not clear what this means)\n"
205            ));
206            // assert_eq!(term.history_use(), hu+1);
207
208            term.append(&format!(
209                "margins = b:{} l:{} r:{} t{}\n",
210                term.margin_bottom(),
211                term.margin_left(),
212                term.margin_right(),
213                term.margin_top()
214            ));
215            assert_eq!(term.margin_bottom(), 3);
216            assert_eq!(term.margin_left(), 3);
217            assert_eq!(term.margin_right(), 3);
218            assert_eq!(term.margin_top(), 3);
219
220            term.set_margin_bottom(5);
221            term.set_margin_left(10);
222            term.set_margin_right(15);
223            term.set_margin_top(20);
224            assert_eq!(term.margin_bottom(), 5);
225            assert_eq!(term.margin_left(), 10);
226            assert_eq!(term.margin_right(), 15);
227            assert_eq!(term.margin_top(), 20);
228
229            term.append("Single character: '");
230            term.print_char('X');
231            term.append("', single UTF-8 character: '");
232            term.print_char_utf8('↑');
233            term.append("'\n");
234
235            let rr = term.redraw_rate();
236            assert_eq!(rr, 0.1, "Default redraw rate at startup");
237            term.append(&format!("Redraw rate {rr}\n"));
238            term.set_redraw_rate(1.0);
239            assert_eq!(term.redraw_rate(), 1.0);
240            term.set_redraw_rate(rr);
241            assert_eq!(term.redraw_rate(), rr);
242
243            let rs = term.redraw_style();
244            term.append(&format!("Redraw style {rs:?}\n"));
245            assert_eq!(
246                rs,
247                RedrawStyle::RateLimited,
248                "Default redraw style at startup"
249            );
250            term.set_redraw_style(RedrawStyle::NoRedraw);
251            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
252            term.set_redraw_style(rs);
253            assert_eq!(term.redraw_style(), rs);
254
255            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
256            assert_eq!(
257                RedrawStyle::NoRedraw.bits(),
258                0x0000,
259                "RedrawStyle enum values have been reassigned"
260            );
261            assert_eq!(
262                RedrawStyle::RateLimited.bits(),
263                0x0001,
264                "RedrawStyle enum values have been reassigned"
265            );
266            assert_eq!(
267                RedrawStyle::PerWrite.bits(),
268                0x0002,
269                "RedrawStyle enum values have been reassigned"
270            );
271
272            let sb = term.scrollbar();
273            let hsb = term.hscrollbar();
274            // Both vertical and horizontal scrollbars are at zero
275            assert_eq!(sb.value(), 0.0);
276            assert_eq!(hsb.value(), 0.0);
277            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
278
279            term.append(&format!(
280                "Scrollbar actual size {}\n",
281                term.scrollbar_actual_size()
282            ));
283            assert_eq!(term.scrollbar_actual_size(), 16);
284            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
285            assert_eq!(
286                term.scrollbar_size(),
287                0,
288                "Default scrollbar size at startup"
289            );
290            term.set_scrollbar_size(40);
291            assert_eq!(term.scrollbar_size(), 40);
292            assert_eq!(term.scrollbar_actual_size(), 40);
293            term.append(&format!(
294                "Scrollbar actual size {}\n",
295                term.scrollbar_actual_size()
296            ));
297            term.set_scrollbar_size(0); // Restore default
298            assert_eq!(term.scrollbar_size(), 0);
299            assert_eq!(term.scrollbar_actual_size(), 16);
300
301            let sfc = term.selection_fg_color();
302            let sbc = term.selection_bg_color();
303            assert_eq!(sfc, Color::Black);
304            assert_eq!(sbc, Color::White);
305            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
306            term.set_selection_fg_color(Color::Green);
307            term.set_selection_bg_color(Color::DarkBlue);
308            assert_eq!(term.selection_fg_color(), Color::Green);
309            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
310            term.set_selection_fg_color(sfc);
311            term.set_selection_bg_color(sbc);
312            assert_eq!(term.selection_fg_color(), Color::Black);
313            assert_eq!(term.selection_bg_color(), Color::White);
314
315            let tfcd = term.text_fg_color_default();
316            let tbcd = term.text_bg_color_default();
317            assert_eq!(tfcd, Color::XtermWhite);
318            assert_eq!(tbcd, Color::TransparentBg);
319            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
320            term.set_text_fg_color_default(Color::Green);
321            term.set_text_bg_color_default(Color::DarkBlue);
322            assert_eq!(term.text_fg_color_default(), Color::Green);
323            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
324            term.set_text_fg_color_default(tfcd);
325            term.set_text_bg_color_default(tbcd);
326            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
327            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
328
329            let tfc = term.text_fg_color();
330            let tbc = term.text_bg_color();
331            assert_eq!(tfc, Color::XtermWhite);
332            assert_eq!(tbc, Color::TransparentBg);
333            term.append(&format!("Text colors: {sfc} {sbc}\n"));
334            term.set_text_fg_color(Color::Green);
335            term.set_text_bg_color(Color::DarkBlue);
336            assert_eq!(term.text_fg_color(), Color::Green);
337            assert_eq!(term.text_bg_color(), Color::DarkBlue);
338            term.set_text_fg_color(tfc);
339            term.set_text_bg_color(tbc);
340            assert_eq!(term.text_fg_color(), Color::XtermWhite);
341            assert_eq!(term.text_bg_color(), Color::TransparentBg);
342
343            let tf = term.text_font();
344            term.append(&format!("Text font: {tf:?}\n"));
345            assert_eq!(tf, Font::Courier);
346            term.set_text_font(Font::Screen);
347            assert_eq!(term.text_font(), Font::Screen);
348            term.set_text_font(tf);
349            assert_eq!(term.text_font(), Font::Courier);
350
351            let ts = term.text_size();
352            let r = term.h_to_row(100);
353            let c = term.w_to_col(100);
354            term.append(&format!(
355                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
356            ));
357            assert_eq!(ts, 14);
358            term.set_text_size(30);
359            assert_eq!(term.text_size(), 30);
360            term.append(&format!(
361                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
362                term.text_size(),
363                term.h_to_row(100),
364                term.w_to_col(100)
365            ));
366            term.set_text_size(ts);
367            assert_eq!(term.text_size(), ts);
368            term.append(&format!(
369                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
370                term.text_size(),
371                term.h_to_row(100),
372                term.w_to_col(100)
373            ));
374
375            // Keyboard handler
376            term.handle({
377                move |term, e| {
378                    match e {
379                        fltk::enums::Event::KeyDown
380                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
381                        {
382                            // false to let FLTK handle ESC. true to hide ESC
383                            false
384                        }
385
386                        fltk::enums::Event::KeyDown
387                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
388                        {
389                            // We handle control keystroke
390                            let k = fltk::app::event_text();
391                            term.append_utf8(&k);
392                            true
393                        }
394
395                        fltk::enums::Event::KeyDown
396                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
397                        {
398                            // We handle normal printable keystroke
399                            let k = fltk::app::event_text();
400                            term.take_focus().unwrap();
401                            term.append(&k);
402                            true
403                        }
404
405                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
406                        // We can do this, or else ignore them (return false)
407                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
408                        //     term.redraw();
409                        //     true
410                        // }
411                        _ => false, // Let FLTK handle everything else
412                    }
413                }
414            });
415
416            let attr_save = term.text_attrib();
417            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
418            term.append("\nStartup tests complete. Keyboard is live.\n");
419            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
420            term.set_text_attrib(attr_save);
421            assert_eq!(term.text_attrib(), attr_save);
422            term.redraw();
423        }
424    });
425
426    app.run().unwrap();
427}
Source

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

Set mouse selection foreground color.

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

pub fn text_bg_color(&self) -> Color

Get the text background color.

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

pub fn text_bg_color_default(&self) -> Color

Return the default text background color.

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

pub fn text_fg_color(&self) -> Color

Get the text foreground color.

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

pub fn text_fg_color_default(&self) -> Color

Return the default text foreground color.

Examples found in repository?
examples/terminal.rs (line 315)
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!(term.text(true), "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");
115
116            // Testing ansi() and set_ansi() methods
117            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
118            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
119            term.set_ansi(false);
120            assert!(!term.ansi());
121            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
122            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
123            term.append_u8(b"Appending u8 array\n");
124            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
125            term.set_ansi(true); // Restore ANSI state
126
127            // Play with the horizontal scrollbar
128            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
129            term.set_hscrollbar_style(ScrollbarStyle::ON);
130            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
131
132            // Test show_unknown() as incidental part of testing append methods
133            term.set_show_unknown(true);
134            assert!(term.show_unknown());
135            term.append_ascii(
136                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
137            );
138            term.set_show_unknown(false);
139            assert!(!term.show_unknown());
140
141            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
142            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
143
144            let r = term.cursor_row();
145            assert_eq!(term.cursor_col(), 0);
146            term.append(&format!("Testing cursor row/col {r}"));
147            assert_eq!(term.cursor_col(), 24);
148            assert_eq!(term.cursor_row(), r);
149
150            // Test cursor color methods
151            assert_eq!(
152                term.cursor_bg_color(),
153                Color::XtermGreen,
154                "Default cursor bg at startup"
155            );
156            assert_eq!(
157                term.cursor_fg_color(),
158                Color::from_hex(0xff_ff_f0),
159                "Default cursor fg at startup"
160            );
161            term.set_cursor_bg_color(Color::Red);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
164            term.set_cursor_fg_color(Color::Blue);
165            assert_eq!(term.cursor_bg_color(), Color::Red);
166            assert_eq!(term.cursor_fg_color(), Color::Blue);
167            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
168            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
169            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
170            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
171
172            // The default display_rows() will derive from the window size
173            let dr = term.display_rows();
174            let height = term.height();
175            assert_eq!(height, term.h());
176            assert!(dr > 20, "Default display_rows at startup");
177            term.resize(term.x(), term.y(), term.w(), height * 2);
178            assert_eq!(term.h(), height * 2);
179            assert_eq!(height * 2, term.h());
180            assert!(term.display_rows() > dr);
181            term.resize(term.x(), term.y(), term.w(), height);
182
183            // The default display_columns() will derive from the window size
184            let dc = term.display_columns();
185            assert!(dc > 80, "Default display_rows at startup");
186            term.set_display_columns(200);
187            assert_eq!(term.display_columns(), 200);
188            term.append("\n         1         2         3         4         5         6         7         8         9");
189            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
190            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
191            term.set_display_columns(90);
192            assert_eq!(term.display_columns(), 90);
193            term.set_display_columns(dc); // Set back to default
194            assert_eq!(term.display_columns(), dc);
195
196            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
197            term.set_history_rows(50);
198            assert_eq!(term.history_rows(), 50);
199            term.set_history_rows(100); // Set back to default
200            assert_eq!(term.history_rows(), 100);
201
202            let hu = term.history_use();
203            term.append(&format!(
204                "history_use = {hu} (it's not clear what this means)\n"
205            ));
206            // assert_eq!(term.history_use(), hu+1);
207
208            term.append(&format!(
209                "margins = b:{} l:{} r:{} t{}\n",
210                term.margin_bottom(),
211                term.margin_left(),
212                term.margin_right(),
213                term.margin_top()
214            ));
215            assert_eq!(term.margin_bottom(), 3);
216            assert_eq!(term.margin_left(), 3);
217            assert_eq!(term.margin_right(), 3);
218            assert_eq!(term.margin_top(), 3);
219
220            term.set_margin_bottom(5);
221            term.set_margin_left(10);
222            term.set_margin_right(15);
223            term.set_margin_top(20);
224            assert_eq!(term.margin_bottom(), 5);
225            assert_eq!(term.margin_left(), 10);
226            assert_eq!(term.margin_right(), 15);
227            assert_eq!(term.margin_top(), 20);
228
229            term.append("Single character: '");
230            term.print_char('X');
231            term.append("', single UTF-8 character: '");
232            term.print_char_utf8('↑');
233            term.append("'\n");
234
235            let rr = term.redraw_rate();
236            assert_eq!(rr, 0.1, "Default redraw rate at startup");
237            term.append(&format!("Redraw rate {rr}\n"));
238            term.set_redraw_rate(1.0);
239            assert_eq!(term.redraw_rate(), 1.0);
240            term.set_redraw_rate(rr);
241            assert_eq!(term.redraw_rate(), rr);
242
243            let rs = term.redraw_style();
244            term.append(&format!("Redraw style {rs:?}\n"));
245            assert_eq!(
246                rs,
247                RedrawStyle::RateLimited,
248                "Default redraw style at startup"
249            );
250            term.set_redraw_style(RedrawStyle::NoRedraw);
251            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
252            term.set_redraw_style(rs);
253            assert_eq!(term.redraw_style(), rs);
254
255            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
256            assert_eq!(
257                RedrawStyle::NoRedraw.bits(),
258                0x0000,
259                "RedrawStyle enum values have been reassigned"
260            );
261            assert_eq!(
262                RedrawStyle::RateLimited.bits(),
263                0x0001,
264                "RedrawStyle enum values have been reassigned"
265            );
266            assert_eq!(
267                RedrawStyle::PerWrite.bits(),
268                0x0002,
269                "RedrawStyle enum values have been reassigned"
270            );
271
272            let sb = term.scrollbar();
273            let hsb = term.hscrollbar();
274            // Both vertical and horizontal scrollbars are at zero
275            assert_eq!(sb.value(), 0.0);
276            assert_eq!(hsb.value(), 0.0);
277            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
278
279            term.append(&format!(
280                "Scrollbar actual size {}\n",
281                term.scrollbar_actual_size()
282            ));
283            assert_eq!(term.scrollbar_actual_size(), 16);
284            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
285            assert_eq!(
286                term.scrollbar_size(),
287                0,
288                "Default scrollbar size at startup"
289            );
290            term.set_scrollbar_size(40);
291            assert_eq!(term.scrollbar_size(), 40);
292            assert_eq!(term.scrollbar_actual_size(), 40);
293            term.append(&format!(
294                "Scrollbar actual size {}\n",
295                term.scrollbar_actual_size()
296            ));
297            term.set_scrollbar_size(0); // Restore default
298            assert_eq!(term.scrollbar_size(), 0);
299            assert_eq!(term.scrollbar_actual_size(), 16);
300
301            let sfc = term.selection_fg_color();
302            let sbc = term.selection_bg_color();
303            assert_eq!(sfc, Color::Black);
304            assert_eq!(sbc, Color::White);
305            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
306            term.set_selection_fg_color(Color::Green);
307            term.set_selection_bg_color(Color::DarkBlue);
308            assert_eq!(term.selection_fg_color(), Color::Green);
309            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
310            term.set_selection_fg_color(sfc);
311            term.set_selection_bg_color(sbc);
312            assert_eq!(term.selection_fg_color(), Color::Black);
313            assert_eq!(term.selection_bg_color(), Color::White);
314
315            let tfcd = term.text_fg_color_default();
316            let tbcd = term.text_bg_color_default();
317            assert_eq!(tfcd, Color::XtermWhite);
318            assert_eq!(tbcd, Color::TransparentBg);
319            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
320            term.set_text_fg_color_default(Color::Green);
321            term.set_text_bg_color_default(Color::DarkBlue);
322            assert_eq!(term.text_fg_color_default(), Color::Green);
323            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
324            term.set_text_fg_color_default(tfcd);
325            term.set_text_bg_color_default(tbcd);
326            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
327            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
328
329            let tfc = term.text_fg_color();
330            let tbc = term.text_bg_color();
331            assert_eq!(tfc, Color::XtermWhite);
332            assert_eq!(tbc, Color::TransparentBg);
333            term.append(&format!("Text colors: {sfc} {sbc}\n"));
334            term.set_text_fg_color(Color::Green);
335            term.set_text_bg_color(Color::DarkBlue);
336            assert_eq!(term.text_fg_color(), Color::Green);
337            assert_eq!(term.text_bg_color(), Color::DarkBlue);
338            term.set_text_fg_color(tfc);
339            term.set_text_bg_color(tbc);
340            assert_eq!(term.text_fg_color(), Color::XtermWhite);
341            assert_eq!(term.text_bg_color(), Color::TransparentBg);
342
343            let tf = term.text_font();
344            term.append(&format!("Text font: {tf:?}\n"));
345            assert_eq!(tf, Font::Courier);
346            term.set_text_font(Font::Screen);
347            assert_eq!(term.text_font(), Font::Screen);
348            term.set_text_font(tf);
349            assert_eq!(term.text_font(), Font::Courier);
350
351            let ts = term.text_size();
352            let r = term.h_to_row(100);
353            let c = term.w_to_col(100);
354            term.append(&format!(
355                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
356            ));
357            assert_eq!(ts, 14);
358            term.set_text_size(30);
359            assert_eq!(term.text_size(), 30);
360            term.append(&format!(
361                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
362                term.text_size(),
363                term.h_to_row(100),
364                term.w_to_col(100)
365            ));
366            term.set_text_size(ts);
367            assert_eq!(term.text_size(), ts);
368            term.append(&format!(
369                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
370                term.text_size(),
371                term.h_to_row(100),
372                term.w_to_col(100)
373            ));
374
375            // Keyboard handler
376            term.handle({
377                move |term, e| {
378                    match e {
379                        fltk::enums::Event::KeyDown
380                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
381                        {
382                            // false to let FLTK handle ESC. true to hide ESC
383                            false
384                        }
385
386                        fltk::enums::Event::KeyDown
387                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
388                        {
389                            // We handle control keystroke
390                            let k = fltk::app::event_text();
391                            term.append_utf8(&k);
392                            true
393                        }
394
395                        fltk::enums::Event::KeyDown
396                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
397                        {
398                            // We handle normal printable keystroke
399                            let k = fltk::app::event_text();
400                            term.take_focus().unwrap();
401                            term.append(&k);
402                            true
403                        }
404
405                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
406                        // We can do this, or else ignore them (return false)
407                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
408                        //     term.redraw();
409                        //     true
410                        // }
411                        _ => false, // Let FLTK handle everything else
412                    }
413                }
414            });
415
416            let attr_save = term.text_attrib();
417            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
418            term.append("\nStartup tests complete. Keyboard is live.\n");
419            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
420            term.set_text_attrib(attr_save);
421            assert_eq!(term.text_attrib(), attr_save);
422            term.redraw();
423        }
424    });
425
426    app.run().unwrap();
427}
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 722)
702fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
703    term.take_focus().unwrap();
704
705    // Test the attr_fg_color and attr_bg_color methods.
706    // Put a single character 'A' into the buffer and check it
707    term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
708    term.set_text_bg_color(Color::TransparentBg);
709    term.set_text_fg_color(Color::XtermWhite);
710    term.append("A");
711    let r = &term.u8c_disp_row(0);
712    let uc = r.col(0);
713    assert_eq!(uc.text_utf8(), b"A");
714    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
715    assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
716    assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
717    assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
718    assert_eq!(&uc.attrib(), &Attrib::Normal);
719
720    // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
721    term.clear();
722    term.set_text_fg_color_xterm(XtermColor::White);
723    term.set_text_bg_color_xterm(XtermColor::Black);
724    assert_eq!(term.text_attrib(), Attrib::Normal);
725
726    assert!(term.ansi());
727    term.append("B\x1b[32mC\x1b[1mD\n");
728
729    let r = &term.u8c_disp_row(0);
730    let uc = r.col(0);
731    assert_eq!(uc.text_utf8(), b"B");
732    assert!(uc.is_char(b'B'));
733    assert!(!uc.is_char(b'A'));
734    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
735    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
736    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
737    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
738    assert_eq!(
739        &uc.charflags(),
740        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
741    );
742
743    let uc = r.col(1);
744    assert_eq!(uc.text_utf8(), b"C");
745    assert!(uc.is_char(b'C'));
746    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
747    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
748    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
749    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
750    assert_eq!(
751        &uc.charflags(),
752        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
753    );
754
755    let uc = r.col(2);
756    assert_eq!(uc.text_utf8(), b"D");
757    assert!(uc.is_char(b'D'));
758    assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
759    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
760    assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
761    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
762    assert_eq!(
763        &uc.attr_fgcolor(Some(term)),
764        &Color::from_rgb(0x20, 0xf0, 0x20)
765    );
766    assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
767    assert_eq!(
768        &uc.charflags(),
769        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
770    );
771
772    // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
773    term.clear();
774    term.set_text_fg_color_xterm(XtermColor::White);
775    term.set_text_bg_color_xterm(XtermColor::Black);
776    term.set_text_attrib(Attrib::Normal);
777    assert_eq!(term.text_attrib(), Attrib::Normal);
778
779    assert!(term.ansi());
780    term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
781
782    let r = &term.u8c_disp_row(0);
783    let uc = r.col(0);
784    assert_eq!(uc.text_utf8(), b"B");
785    assert!(uc.is_char(b'B'));
786    assert!(!uc.is_char(b'A'));
787    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
788    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
789    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
790    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
791    assert_eq!(
792        &uc.charflags(),
793        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
794    );
795
796    let uc = r.col(1);
797    assert_eq!(uc.text_utf8(), b"C");
798    assert!(uc.is_char(b'C'));
799    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
800    assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
801    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
802    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
803    assert_eq!(
804        &uc.charflags(),
805        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
806    );
807
808    let uc = r.col(2);
809    assert_eq!(uc.text_utf8(), b"D");
810    assert!(uc.is_char(b'D'));
811    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
812    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
813    assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
814    assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
815    assert_eq!(
816        &uc.charflags(),
817        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
818    );
819
820    let uc = r.col(3);
821    assert_eq!(uc.text_utf8(), b"E");
822    assert!(uc.is_char(b'E'));
823    assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
824    assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
825    assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
826    assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
827    assert_eq!(
828        &uc.charflags(),
829        &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
830    );
831
832    // Test some miscellaneous Utf8 constants
833    assert_eq!(uc.length(), 1);
834    assert_eq!(uc.max_utf8(), 4);
835    assert_eq!(uc.pwidth(), 9.0);
836    assert_eq!(uc.pwidth_int(), 9);
837
838    term.set_text_fg_color_xterm(XtermColor::White);
839    term.set_text_bg_color_xterm(XtermColor::Black);
840    term.clear();
841    term.set_text_attrib(Attrib::Normal);
842
843    // Mouse selection functions
844    term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
845    term.clear_mouse_selection();
846    assert_eq!(term.get_selection(), None);
847
848    // Play with cursor position
849    term.append("0123456789\n"); // Set up test pattern
850    term.append("ABCDEFGHIJ\n");
851    term.append("abcdefghij\n");
852
853    term.set_cursor_row(1);
854    assert_eq!(term.cursor_row(), 1);
855    term.set_cursor_col(1);
856    assert_eq!(term.cursor_col(), 1);
857    assert_eq!(term.u8c_cursor().text_utf8(), b"1");
858
859    term.append("----"); // Overwrites text at cursor and moves cursor forward
860    assert_eq!(term.cursor_row(), 1);
861    assert_eq!(term.cursor_col(), 5);
862    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
863    term.set_cursor_col(1);
864    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
865
866    term.cursor_up(1, false);
867    assert_eq!(term.cursor_row(), 0);
868    assert_eq!(term.cursor_col(), 1);
869    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
870
871    // Hit top of screen, so nothing happens
872    term.cursor_up(1, false);
873    assert_eq!(term.cursor_row(), 0);
874    assert_eq!(term.cursor_col(), 1);
875    assert_eq!(term.u8c_cursor().text_utf8(), b"o");
876
877    // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
878    term.cursor_up(1, true);
879    assert_eq!(term.cursor_row(), 0);
880    assert_eq!(term.cursor_col(), 1);
881    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
882
883    // Go back down to the overwritten text
884    term.cursor_down(2, false);
885    assert_eq!(term.cursor_row(), 2);
886    assert_eq!(term.cursor_col(), 1);
887    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
888
889    // Go right past the overwritten text
890    term.cursor_right(4, false);
891    assert_eq!(term.cursor_row(), 2);
892    assert_eq!(term.cursor_col(), 5);
893    assert_eq!(term.u8c_cursor().text_utf8(), b"5");
894
895    // Go left to the end of the overwritten text
896    term.cursor_left(1);
897    assert_eq!(term.cursor_row(), 2);
898    assert_eq!(term.cursor_col(), 4);
899    assert_eq!(term.u8c_cursor().text_utf8(), b"-");
900
901    // Scroll back down, removing the blank line at the top.
902    // Cursor stays in place, the text moves under it.
903    term.scroll(1);
904    assert_eq!(term.cursor_row(), 2);
905    assert_eq!(term.cursor_col(), 4);
906    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
907
908    // Clear from here to end-of-line
909    term.clear_eol();
910    assert_eq!(term.cursor_row(), 2);
911    assert_eq!(term.cursor_col(), 4);
912    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
913
914    // Now clear from here to start-of-line. Cursor does not move.
915    term.clear_sol();
916    assert_eq!(term.cursor_row(), 2);
917    assert_eq!(term.cursor_col(), 4);
918    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
919    term.cursor_left(1);
920    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
921    term.set_cursor_col(0);
922    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
923
924    // Clear some lines
925    term.clear_line(1);
926    assert_eq!(term.cursor_row(), 2);
927    assert_eq!(term.cursor_col(), 0);
928    term.set_cursor_row(1);
929    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930    term.set_cursor_row(3);
931    term.clear_cur_line();
932    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
933    assert_eq!(term.cursor_row(), 3);
934    assert_eq!(term.cursor_col(), 0);
935
936    term.append("Two lines above are intentionally left blank.\n");
937    assert_eq!(term.cursor_row(), 4);
938    assert_eq!(term.cursor_col(), 0);
939
940    // Set up the test pattern again, then play with insert/delete
941    term.append("0123456789\n");
942    term.append("ABCDEFGHIJ\n");
943    term.append("abcdefghij\n");
944    assert_eq!(term.cursor_row(), 7);
945
946    term.set_cursor_row(4);
947    term.set_cursor_col(4);
948    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
949
950    term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
951    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
952    term.cursor_right(5, false);
953    assert_eq!(term.cursor_col(), 9);
954    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
955
956    // Insert two blank rows above cursor. Cursor stays put.
957    term.insert_rows(2);
958    assert_eq!(term.cursor_row(), 4);
959    assert_eq!(term.cursor_col(), 9);
960    assert_eq!(term.u8c_cursor().text_utf8(), b" ");
961    term.cursor_down(2, false); // Go down to find our text again
962    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
963
964    // Go back to the beginning of the inserted 'x' characters and delete them.
965    term.cursor_left(5);
966    assert_eq!(term.u8c_cursor().text_utf8(), b"x");
967    term.delete_cur_chars(5);
968    assert_eq!(term.cursor_row(), 6);
969    assert_eq!(term.cursor_col(), 4);
970    assert_eq!(term.u8c_cursor().text_utf8(), b"4");
971
972    term.delete_chars(7, 2, 2); // Delete "CD" from the next row
973    term.cursor_down(1, false);
974    term.cursor_left(2);
975    assert_eq!(term.u8c_cursor().text_utf8(), b"E");
976
977    term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
978    assert_eq!(term.u8c_cursor().text_utf8(), b"c");
979    term.cursor_up(1, false);
980    term.delete_rows(2); // Delete remains of test pattern
981
982    term.set_text_attrib(Attrib::Bold);
983    term.insert_char_eol('-', 3, 15, 20);
984    term.set_cursor_row(3);
985    term.set_cursor_col(15);
986    assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
987    assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
988
989    term.set_text_attrib(Attrib::Italic);
990    term.append(" and all lines below");
991    term.set_text_attrib(Attrib::Normal);
992    term.cursor_down(1, false);
993
994    let mut hsb = term.hscrollbar();
995    let mut sb = term.scrollbar();
996    hsb.set_value(100.0);
997    assert_eq!(hsb.value(), 100.0);
998    sb.set_value(50.0);
999    assert_eq!(sb.value(), 50.0);
1000    hsb.set_value(0.0);
1001    assert_eq!(hsb.value(), 0.0);
1002    sb.set_value(0.0);
1003    assert_eq!(sb.value(), 0.0);
1004}
Source

pub fn text_font(&self) -> Font

Get the text font

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!(term.text(true), "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");
115
116            // Testing ansi() and set_ansi() methods
117            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
118            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
119            term.set_ansi(false);
120            assert!(!term.ansi());
121            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
122            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
123            term.append_u8(b"Appending u8 array\n");
124            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
125            term.set_ansi(true); // Restore ANSI state
126
127            // Play with the horizontal scrollbar
128            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
129            term.set_hscrollbar_style(ScrollbarStyle::ON);
130            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
131
132            // Test show_unknown() as incidental part of testing append methods
133            term.set_show_unknown(true);
134            assert!(term.show_unknown());
135            term.append_ascii(
136                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
137            );
138            term.set_show_unknown(false);
139            assert!(!term.show_unknown());
140
141            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
142            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
143
144            let r = term.cursor_row();
145            assert_eq!(term.cursor_col(), 0);
146            term.append(&format!("Testing cursor row/col {r}"));
147            assert_eq!(term.cursor_col(), 24);
148            assert_eq!(term.cursor_row(), r);
149
150            // Test cursor color methods
151            assert_eq!(
152                term.cursor_bg_color(),
153                Color::XtermGreen,
154                "Default cursor bg at startup"
155            );
156            assert_eq!(
157                term.cursor_fg_color(),
158                Color::from_hex(0xff_ff_f0),
159                "Default cursor fg at startup"
160            );
161            term.set_cursor_bg_color(Color::Red);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
164            term.set_cursor_fg_color(Color::Blue);
165            assert_eq!(term.cursor_bg_color(), Color::Red);
166            assert_eq!(term.cursor_fg_color(), Color::Blue);
167            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
168            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
169            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
170            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
171
172            // The default display_rows() will derive from the window size
173            let dr = term.display_rows();
174            let height = term.height();
175            assert_eq!(height, term.h());
176            assert!(dr > 20, "Default display_rows at startup");
177            term.resize(term.x(), term.y(), term.w(), height * 2);
178            assert_eq!(term.h(), height * 2);
179            assert_eq!(height * 2, term.h());
180            assert!(term.display_rows() > dr);
181            term.resize(term.x(), term.y(), term.w(), height);
182
183            // The default display_columns() will derive from the window size
184            let dc = term.display_columns();
185            assert!(dc > 80, "Default display_rows at startup");
186            term.set_display_columns(200);
187            assert_eq!(term.display_columns(), 200);
188            term.append("\n         1         2         3         4         5         6         7         8         9");
189            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
190            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
191            term.set_display_columns(90);
192            assert_eq!(term.display_columns(), 90);
193            term.set_display_columns(dc); // Set back to default
194            assert_eq!(term.display_columns(), dc);
195
196            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
197            term.set_history_rows(50);
198            assert_eq!(term.history_rows(), 50);
199            term.set_history_rows(100); // Set back to default
200            assert_eq!(term.history_rows(), 100);
201
202            let hu = term.history_use();
203            term.append(&format!(
204                "history_use = {hu} (it's not clear what this means)\n"
205            ));
206            // assert_eq!(term.history_use(), hu+1);
207
208            term.append(&format!(
209                "margins = b:{} l:{} r:{} t{}\n",
210                term.margin_bottom(),
211                term.margin_left(),
212                term.margin_right(),
213                term.margin_top()
214            ));
215            assert_eq!(term.margin_bottom(), 3);
216            assert_eq!(term.margin_left(), 3);
217            assert_eq!(term.margin_right(), 3);
218            assert_eq!(term.margin_top(), 3);
219
220            term.set_margin_bottom(5);
221            term.set_margin_left(10);
222            term.set_margin_right(15);
223            term.set_margin_top(20);
224            assert_eq!(term.margin_bottom(), 5);
225            assert_eq!(term.margin_left(), 10);
226            assert_eq!(term.margin_right(), 15);
227            assert_eq!(term.margin_top(), 20);
228
229            term.append("Single character: '");
230            term.print_char('X');
231            term.append("', single UTF-8 character: '");
232            term.print_char_utf8('↑');
233            term.append("'\n");
234
235            let rr = term.redraw_rate();
236            assert_eq!(rr, 0.1, "Default redraw rate at startup");
237            term.append(&format!("Redraw rate {rr}\n"));
238            term.set_redraw_rate(1.0);
239            assert_eq!(term.redraw_rate(), 1.0);
240            term.set_redraw_rate(rr);
241            assert_eq!(term.redraw_rate(), rr);
242
243            let rs = term.redraw_style();
244            term.append(&format!("Redraw style {rs:?}\n"));
245            assert_eq!(
246                rs,
247                RedrawStyle::RateLimited,
248                "Default redraw style at startup"
249            );
250            term.set_redraw_style(RedrawStyle::NoRedraw);
251            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
252            term.set_redraw_style(rs);
253            assert_eq!(term.redraw_style(), rs);
254
255            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
256            assert_eq!(
257                RedrawStyle::NoRedraw.bits(),
258                0x0000,
259                "RedrawStyle enum values have been reassigned"
260            );
261            assert_eq!(
262                RedrawStyle::RateLimited.bits(),
263                0x0001,
264                "RedrawStyle enum values have been reassigned"
265            );
266            assert_eq!(
267                RedrawStyle::PerWrite.bits(),
268                0x0002,
269                "RedrawStyle enum values have been reassigned"
270            );
271
272            let sb = term.scrollbar();
273            let hsb = term.hscrollbar();
274            // Both vertical and horizontal scrollbars are at zero
275            assert_eq!(sb.value(), 0.0);
276            assert_eq!(hsb.value(), 0.0);
277            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
278
279            term.append(&format!(
280                "Scrollbar actual size {}\n",
281                term.scrollbar_actual_size()
282            ));
283            assert_eq!(term.scrollbar_actual_size(), 16);
284            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
285            assert_eq!(
286                term.scrollbar_size(),
287                0,
288                "Default scrollbar size at startup"
289            );
290            term.set_scrollbar_size(40);
291            assert_eq!(term.scrollbar_size(), 40);
292            assert_eq!(term.scrollbar_actual_size(), 40);
293            term.append(&format!(
294                "Scrollbar actual size {}\n",
295                term.scrollbar_actual_size()
296            ));
297            term.set_scrollbar_size(0); // Restore default
298            assert_eq!(term.scrollbar_size(), 0);
299            assert_eq!(term.scrollbar_actual_size(), 16);
300
301            let sfc = term.selection_fg_color();
302            let sbc = term.selection_bg_color();
303            assert_eq!(sfc, Color::Black);
304            assert_eq!(sbc, Color::White);
305            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
306            term.set_selection_fg_color(Color::Green);
307            term.set_selection_bg_color(Color::DarkBlue);
308            assert_eq!(term.selection_fg_color(), Color::Green);
309            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
310            term.set_selection_fg_color(sfc);
311            term.set_selection_bg_color(sbc);
312            assert_eq!(term.selection_fg_color(), Color::Black);
313            assert_eq!(term.selection_bg_color(), Color::White);
314
315            let tfcd = term.text_fg_color_default();
316            let tbcd = term.text_bg_color_default();
317            assert_eq!(tfcd, Color::XtermWhite);
318            assert_eq!(tbcd, Color::TransparentBg);
319            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
320            term.set_text_fg_color_default(Color::Green);
321            term.set_text_bg_color_default(Color::DarkBlue);
322            assert_eq!(term.text_fg_color_default(), Color::Green);
323            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
324            term.set_text_fg_color_default(tfcd);
325            term.set_text_bg_color_default(tbcd);
326            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
327            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
328
329            let tfc = term.text_fg_color();
330            let tbc = term.text_bg_color();
331            assert_eq!(tfc, Color::XtermWhite);
332            assert_eq!(tbc, Color::TransparentBg);
333            term.append(&format!("Text colors: {sfc} {sbc}\n"));
334            term.set_text_fg_color(Color::Green);
335            term.set_text_bg_color(Color::DarkBlue);
336            assert_eq!(term.text_fg_color(), Color::Green);
337            assert_eq!(term.text_bg_color(), Color::DarkBlue);
338            term.set_text_fg_color(tfc);
339            term.set_text_bg_color(tbc);
340            assert_eq!(term.text_fg_color(), Color::XtermWhite);
341            assert_eq!(term.text_bg_color(), Color::TransparentBg);
342
343            let tf = term.text_font();
344            term.append(&format!("Text font: {tf:?}\n"));
345            assert_eq!(tf, Font::Courier);
346            term.set_text_font(Font::Screen);
347            assert_eq!(term.text_font(), Font::Screen);
348            term.set_text_font(tf);
349            assert_eq!(term.text_font(), Font::Courier);
350
351            let ts = term.text_size();
352            let r = term.h_to_row(100);
353            let c = term.w_to_col(100);
354            term.append(&format!(
355                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
356            ));
357            assert_eq!(ts, 14);
358            term.set_text_size(30);
359            assert_eq!(term.text_size(), 30);
360            term.append(&format!(
361                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
362                term.text_size(),
363                term.h_to_row(100),
364                term.w_to_col(100)
365            ));
366            term.set_text_size(ts);
367            assert_eq!(term.text_size(), ts);
368            term.append(&format!(
369                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
370                term.text_size(),
371                term.h_to_row(100),
372                term.w_to_col(100)
373            ));
374
375            // Keyboard handler
376            term.handle({
377                move |term, e| {
378                    match e {
379                        fltk::enums::Event::KeyDown
380                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
381                        {
382                            // false to let FLTK handle ESC. true to hide ESC
383                            false
384                        }
385
386                        fltk::enums::Event::KeyDown
387                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
388                        {
389                            // We handle control keystroke
390                            let k = fltk::app::event_text();
391                            term.append_utf8(&k);
392                            true
393                        }
394
395                        fltk::enums::Event::KeyDown
396                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
397                        {
398                            // We handle normal printable keystroke
399                            let k = fltk::app::event_text();
400                            term.take_focus().unwrap();
401                            term.append(&k);
402                            true
403                        }
404
405                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
406                        // We can do this, or else ignore them (return false)
407                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
408                        //     term.redraw();
409                        //     true
410                        // }
411                        _ => false, // Let FLTK handle everything else
412                    }
413                }
414            });
415
416            let attr_save = term.text_attrib();
417            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
418            term.append("\nStartup tests complete. Keyboard is live.\n");
419            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
420            term.set_text_attrib(attr_save);
421            assert_eq!(term.text_attrib(), attr_save);
422            term.redraw();
423        }
424    });
425
426    app.run().unwrap();
427}
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 346)
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!(term.text(true), "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");
115
116            // Testing ansi() and set_ansi() methods
117            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
118            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
119            term.set_ansi(false);
120            assert!(!term.ansi());
121            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
122            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
123            term.append_u8(b"Appending u8 array\n");
124            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
125            term.set_ansi(true); // Restore ANSI state
126
127            // Play with the horizontal scrollbar
128            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
129            term.set_hscrollbar_style(ScrollbarStyle::ON);
130            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
131
132            // Test show_unknown() as incidental part of testing append methods
133            term.set_show_unknown(true);
134            assert!(term.show_unknown());
135            term.append_ascii(
136                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
137            );
138            term.set_show_unknown(false);
139            assert!(!term.show_unknown());
140
141            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
142            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
143
144            let r = term.cursor_row();
145            assert_eq!(term.cursor_col(), 0);
146            term.append(&format!("Testing cursor row/col {r}"));
147            assert_eq!(term.cursor_col(), 24);
148            assert_eq!(term.cursor_row(), r);
149
150            // Test cursor color methods
151            assert_eq!(
152                term.cursor_bg_color(),
153                Color::XtermGreen,
154                "Default cursor bg at startup"
155            );
156            assert_eq!(
157                term.cursor_fg_color(),
158                Color::from_hex(0xff_ff_f0),
159                "Default cursor fg at startup"
160            );
161            term.set_cursor_bg_color(Color::Red);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
164            term.set_cursor_fg_color(Color::Blue);
165            assert_eq!(term.cursor_bg_color(), Color::Red);
166            assert_eq!(term.cursor_fg_color(), Color::Blue);
167            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
168            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
169            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
170            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
171
172            // The default display_rows() will derive from the window size
173            let dr = term.display_rows();
174            let height = term.height();
175            assert_eq!(height, term.h());
176            assert!(dr > 20, "Default display_rows at startup");
177            term.resize(term.x(), term.y(), term.w(), height * 2);
178            assert_eq!(term.h(), height * 2);
179            assert_eq!(height * 2, term.h());
180            assert!(term.display_rows() > dr);
181            term.resize(term.x(), term.y(), term.w(), height);
182
183            // The default display_columns() will derive from the window size
184            let dc = term.display_columns();
185            assert!(dc > 80, "Default display_rows at startup");
186            term.set_display_columns(200);
187            assert_eq!(term.display_columns(), 200);
188            term.append("\n         1         2         3         4         5         6         7         8         9");
189            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
190            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
191            term.set_display_columns(90);
192            assert_eq!(term.display_columns(), 90);
193            term.set_display_columns(dc); // Set back to default
194            assert_eq!(term.display_columns(), dc);
195
196            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
197            term.set_history_rows(50);
198            assert_eq!(term.history_rows(), 50);
199            term.set_history_rows(100); // Set back to default
200            assert_eq!(term.history_rows(), 100);
201
202            let hu = term.history_use();
203            term.append(&format!(
204                "history_use = {hu} (it's not clear what this means)\n"
205            ));
206            // assert_eq!(term.history_use(), hu+1);
207
208            term.append(&format!(
209                "margins = b:{} l:{} r:{} t{}\n",
210                term.margin_bottom(),
211                term.margin_left(),
212                term.margin_right(),
213                term.margin_top()
214            ));
215            assert_eq!(term.margin_bottom(), 3);
216            assert_eq!(term.margin_left(), 3);
217            assert_eq!(term.margin_right(), 3);
218            assert_eq!(term.margin_top(), 3);
219
220            term.set_margin_bottom(5);
221            term.set_margin_left(10);
222            term.set_margin_right(15);
223            term.set_margin_top(20);
224            assert_eq!(term.margin_bottom(), 5);
225            assert_eq!(term.margin_left(), 10);
226            assert_eq!(term.margin_right(), 15);
227            assert_eq!(term.margin_top(), 20);
228
229            term.append("Single character: '");
230            term.print_char('X');
231            term.append("', single UTF-8 character: '");
232            term.print_char_utf8('↑');
233            term.append("'\n");
234
235            let rr = term.redraw_rate();
236            assert_eq!(rr, 0.1, "Default redraw rate at startup");
237            term.append(&format!("Redraw rate {rr}\n"));
238            term.set_redraw_rate(1.0);
239            assert_eq!(term.redraw_rate(), 1.0);
240            term.set_redraw_rate(rr);
241            assert_eq!(term.redraw_rate(), rr);
242
243            let rs = term.redraw_style();
244            term.append(&format!("Redraw style {rs:?}\n"));
245            assert_eq!(
246                rs,
247                RedrawStyle::RateLimited,
248                "Default redraw style at startup"
249            );
250            term.set_redraw_style(RedrawStyle::NoRedraw);
251            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
252            term.set_redraw_style(rs);
253            assert_eq!(term.redraw_style(), rs);
254
255            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
256            assert_eq!(
257                RedrawStyle::NoRedraw.bits(),
258                0x0000,
259                "RedrawStyle enum values have been reassigned"
260            );
261            assert_eq!(
262                RedrawStyle::RateLimited.bits(),
263                0x0001,
264                "RedrawStyle enum values have been reassigned"
265            );
266            assert_eq!(
267                RedrawStyle::PerWrite.bits(),
268                0x0002,
269                "RedrawStyle enum values have been reassigned"
270            );
271
272            let sb = term.scrollbar();
273            let hsb = term.hscrollbar();
274            // Both vertical and horizontal scrollbars are at zero
275            assert_eq!(sb.value(), 0.0);
276            assert_eq!(hsb.value(), 0.0);
277            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
278
279            term.append(&format!(
280                "Scrollbar actual size {}\n",
281                term.scrollbar_actual_size()
282            ));
283            assert_eq!(term.scrollbar_actual_size(), 16);
284            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
285            assert_eq!(
286                term.scrollbar_size(),
287                0,
288                "Default scrollbar size at startup"
289            );
290            term.set_scrollbar_size(40);
291            assert_eq!(term.scrollbar_size(), 40);
292            assert_eq!(term.scrollbar_actual_size(), 40);
293            term.append(&format!(
294                "Scrollbar actual size {}\n",
295                term.scrollbar_actual_size()
296            ));
297            term.set_scrollbar_size(0); // Restore default
298            assert_eq!(term.scrollbar_size(), 0);
299            assert_eq!(term.scrollbar_actual_size(), 16);
300
301            let sfc = term.selection_fg_color();
302            let sbc = term.selection_bg_color();
303            assert_eq!(sfc, Color::Black);
304            assert_eq!(sbc, Color::White);
305            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
306            term.set_selection_fg_color(Color::Green);
307            term.set_selection_bg_color(Color::DarkBlue);
308            assert_eq!(term.selection_fg_color(), Color::Green);
309            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
310            term.set_selection_fg_color(sfc);
311            term.set_selection_bg_color(sbc);
312            assert_eq!(term.selection_fg_color(), Color::Black);
313            assert_eq!(term.selection_bg_color(), Color::White);
314
315            let tfcd = term.text_fg_color_default();
316            let tbcd = term.text_bg_color_default();
317            assert_eq!(tfcd, Color::XtermWhite);
318            assert_eq!(tbcd, Color::TransparentBg);
319            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
320            term.set_text_fg_color_default(Color::Green);
321            term.set_text_bg_color_default(Color::DarkBlue);
322            assert_eq!(term.text_fg_color_default(), Color::Green);
323            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
324            term.set_text_fg_color_default(tfcd);
325            term.set_text_bg_color_default(tbcd);
326            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
327            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
328
329            let tfc = term.text_fg_color();
330            let tbc = term.text_bg_color();
331            assert_eq!(tfc, Color::XtermWhite);
332            assert_eq!(tbc, Color::TransparentBg);
333            term.append(&format!("Text colors: {sfc} {sbc}\n"));
334            term.set_text_fg_color(Color::Green);
335            term.set_text_bg_color(Color::DarkBlue);
336            assert_eq!(term.text_fg_color(), Color::Green);
337            assert_eq!(term.text_bg_color(), Color::DarkBlue);
338            term.set_text_fg_color(tfc);
339            term.set_text_bg_color(tbc);
340            assert_eq!(term.text_fg_color(), Color::XtermWhite);
341            assert_eq!(term.text_bg_color(), Color::TransparentBg);
342
343            let tf = term.text_font();
344            term.append(&format!("Text font: {tf:?}\n"));
345            assert_eq!(tf, Font::Courier);
346            term.set_text_font(Font::Screen);
347            assert_eq!(term.text_font(), Font::Screen);
348            term.set_text_font(tf);
349            assert_eq!(term.text_font(), Font::Courier);
350
351            let ts = term.text_size();
352            let r = term.h_to_row(100);
353            let c = term.w_to_col(100);
354            term.append(&format!(
355                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
356            ));
357            assert_eq!(ts, 14);
358            term.set_text_size(30);
359            assert_eq!(term.text_size(), 30);
360            term.append(&format!(
361                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
362                term.text_size(),
363                term.h_to_row(100),
364                term.w_to_col(100)
365            ));
366            term.set_text_size(ts);
367            assert_eq!(term.text_size(), ts);
368            term.append(&format!(
369                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
370                term.text_size(),
371                term.h_to_row(100),
372                term.w_to_col(100)
373            ));
374
375            // Keyboard handler
376            term.handle({
377                move |term, e| {
378                    match e {
379                        fltk::enums::Event::KeyDown
380                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
381                        {
382                            // false to let FLTK handle ESC. true to hide ESC
383                            false
384                        }
385
386                        fltk::enums::Event::KeyDown
387                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
388                        {
389                            // We handle control keystroke
390                            let k = fltk::app::event_text();
391                            term.append_utf8(&k);
392                            true
393                        }
394
395                        fltk::enums::Event::KeyDown
396                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
397                        {
398                            // We handle normal printable keystroke
399                            let k = fltk::app::event_text();
400                            term.take_focus().unwrap();
401                            term.append(&k);
402                            true
403                        }
404
405                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
406                        // We can do this, or else ignore them (return false)
407                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
408                        //     term.redraw();
409                        //     true
410                        // }
411                        _ => false, // Let FLTK handle everything else
412                    }
413                }
414            });
415
416            let attr_save = term.text_attrib();
417            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
418            term.append("\nStartup tests complete. Keyboard is live.\n");
419            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
420            term.set_text_attrib(attr_save);
421            assert_eq!(term.text_attrib(), attr_save);
422            term.redraw();
423        }
424    });
425
426    app.run().unwrap();
427}
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 351)
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!(term.text(true), "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");
115
116            // Testing ansi() and set_ansi() methods
117            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
118            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
119            term.set_ansi(false);
120            assert!(!term.ansi());
121            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
122            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
123            term.append_u8(b"Appending u8 array\n");
124            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
125            term.set_ansi(true); // Restore ANSI state
126
127            // Play with the horizontal scrollbar
128            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
129            term.set_hscrollbar_style(ScrollbarStyle::ON);
130            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
131
132            // Test show_unknown() as incidental part of testing append methods
133            term.set_show_unknown(true);
134            assert!(term.show_unknown());
135            term.append_ascii(
136                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
137            );
138            term.set_show_unknown(false);
139            assert!(!term.show_unknown());
140
141            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
142            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
143
144            let r = term.cursor_row();
145            assert_eq!(term.cursor_col(), 0);
146            term.append(&format!("Testing cursor row/col {r}"));
147            assert_eq!(term.cursor_col(), 24);
148            assert_eq!(term.cursor_row(), r);
149
150            // Test cursor color methods
151            assert_eq!(
152                term.cursor_bg_color(),
153                Color::XtermGreen,
154                "Default cursor bg at startup"
155            );
156            assert_eq!(
157                term.cursor_fg_color(),
158                Color::from_hex(0xff_ff_f0),
159                "Default cursor fg at startup"
160            );
161            term.set_cursor_bg_color(Color::Red);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
164            term.set_cursor_fg_color(Color::Blue);
165            assert_eq!(term.cursor_bg_color(), Color::Red);
166            assert_eq!(term.cursor_fg_color(), Color::Blue);
167            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
168            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
169            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
170            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
171
172            // The default display_rows() will derive from the window size
173            let dr = term.display_rows();
174            let height = term.height();
175            assert_eq!(height, term.h());
176            assert!(dr > 20, "Default display_rows at startup");
177            term.resize(term.x(), term.y(), term.w(), height * 2);
178            assert_eq!(term.h(), height * 2);
179            assert_eq!(height * 2, term.h());
180            assert!(term.display_rows() > dr);
181            term.resize(term.x(), term.y(), term.w(), height);
182
183            // The default display_columns() will derive from the window size
184            let dc = term.display_columns();
185            assert!(dc > 80, "Default display_rows at startup");
186            term.set_display_columns(200);
187            assert_eq!(term.display_columns(), 200);
188            term.append("\n         1         2         3         4         5         6         7         8         9");
189            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
190            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
191            term.set_display_columns(90);
192            assert_eq!(term.display_columns(), 90);
193            term.set_display_columns(dc); // Set back to default
194            assert_eq!(term.display_columns(), dc);
195
196            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
197            term.set_history_rows(50);
198            assert_eq!(term.history_rows(), 50);
199            term.set_history_rows(100); // Set back to default
200            assert_eq!(term.history_rows(), 100);
201
202            let hu = term.history_use();
203            term.append(&format!(
204                "history_use = {hu} (it's not clear what this means)\n"
205            ));
206            // assert_eq!(term.history_use(), hu+1);
207
208            term.append(&format!(
209                "margins = b:{} l:{} r:{} t{}\n",
210                term.margin_bottom(),
211                term.margin_left(),
212                term.margin_right(),
213                term.margin_top()
214            ));
215            assert_eq!(term.margin_bottom(), 3);
216            assert_eq!(term.margin_left(), 3);
217            assert_eq!(term.margin_right(), 3);
218            assert_eq!(term.margin_top(), 3);
219
220            term.set_margin_bottom(5);
221            term.set_margin_left(10);
222            term.set_margin_right(15);
223            term.set_margin_top(20);
224            assert_eq!(term.margin_bottom(), 5);
225            assert_eq!(term.margin_left(), 10);
226            assert_eq!(term.margin_right(), 15);
227            assert_eq!(term.margin_top(), 20);
228
229            term.append("Single character: '");
230            term.print_char('X');
231            term.append("', single UTF-8 character: '");
232            term.print_char_utf8('↑');
233            term.append("'\n");
234
235            let rr = term.redraw_rate();
236            assert_eq!(rr, 0.1, "Default redraw rate at startup");
237            term.append(&format!("Redraw rate {rr}\n"));
238            term.set_redraw_rate(1.0);
239            assert_eq!(term.redraw_rate(), 1.0);
240            term.set_redraw_rate(rr);
241            assert_eq!(term.redraw_rate(), rr);
242
243            let rs = term.redraw_style();
244            term.append(&format!("Redraw style {rs:?}\n"));
245            assert_eq!(
246                rs,
247                RedrawStyle::RateLimited,
248                "Default redraw style at startup"
249            );
250            term.set_redraw_style(RedrawStyle::NoRedraw);
251            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
252            term.set_redraw_style(rs);
253            assert_eq!(term.redraw_style(), rs);
254
255            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
256            assert_eq!(
257                RedrawStyle::NoRedraw.bits(),
258                0x0000,
259                "RedrawStyle enum values have been reassigned"
260            );
261            assert_eq!(
262                RedrawStyle::RateLimited.bits(),
263                0x0001,
264                "RedrawStyle enum values have been reassigned"
265            );
266            assert_eq!(
267                RedrawStyle::PerWrite.bits(),
268                0x0002,
269                "RedrawStyle enum values have been reassigned"
270            );
271
272            let sb = term.scrollbar();
273            let hsb = term.hscrollbar();
274            // Both vertical and horizontal scrollbars are at zero
275            assert_eq!(sb.value(), 0.0);
276            assert_eq!(hsb.value(), 0.0);
277            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
278
279            term.append(&format!(
280                "Scrollbar actual size {}\n",
281                term.scrollbar_actual_size()
282            ));
283            assert_eq!(term.scrollbar_actual_size(), 16);
284            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
285            assert_eq!(
286                term.scrollbar_size(),
287                0,
288                "Default scrollbar size at startup"
289            );
290            term.set_scrollbar_size(40);
291            assert_eq!(term.scrollbar_size(), 40);
292            assert_eq!(term.scrollbar_actual_size(), 40);
293            term.append(&format!(
294                "Scrollbar actual size {}\n",
295                term.scrollbar_actual_size()
296            ));
297            term.set_scrollbar_size(0); // Restore default
298            assert_eq!(term.scrollbar_size(), 0);
299            assert_eq!(term.scrollbar_actual_size(), 16);
300
301            let sfc = term.selection_fg_color();
302            let sbc = term.selection_bg_color();
303            assert_eq!(sfc, Color::Black);
304            assert_eq!(sbc, Color::White);
305            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
306            term.set_selection_fg_color(Color::Green);
307            term.set_selection_bg_color(Color::DarkBlue);
308            assert_eq!(term.selection_fg_color(), Color::Green);
309            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
310            term.set_selection_fg_color(sfc);
311            term.set_selection_bg_color(sbc);
312            assert_eq!(term.selection_fg_color(), Color::Black);
313            assert_eq!(term.selection_bg_color(), Color::White);
314
315            let tfcd = term.text_fg_color_default();
316            let tbcd = term.text_bg_color_default();
317            assert_eq!(tfcd, Color::XtermWhite);
318            assert_eq!(tbcd, Color::TransparentBg);
319            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
320            term.set_text_fg_color_default(Color::Green);
321            term.set_text_bg_color_default(Color::DarkBlue);
322            assert_eq!(term.text_fg_color_default(), Color::Green);
323            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
324            term.set_text_fg_color_default(tfcd);
325            term.set_text_bg_color_default(tbcd);
326            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
327            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
328
329            let tfc = term.text_fg_color();
330            let tbc = term.text_bg_color();
331            assert_eq!(tfc, Color::XtermWhite);
332            assert_eq!(tbc, Color::TransparentBg);
333            term.append(&format!("Text colors: {sfc} {sbc}\n"));
334            term.set_text_fg_color(Color::Green);
335            term.set_text_bg_color(Color::DarkBlue);
336            assert_eq!(term.text_fg_color(), Color::Green);
337            assert_eq!(term.text_bg_color(), Color::DarkBlue);
338            term.set_text_fg_color(tfc);
339            term.set_text_bg_color(tbc);
340            assert_eq!(term.text_fg_color(), Color::XtermWhite);
341            assert_eq!(term.text_bg_color(), Color::TransparentBg);
342
343            let tf = term.text_font();
344            term.append(&format!("Text font: {tf:?}\n"));
345            assert_eq!(tf, Font::Courier);
346            term.set_text_font(Font::Screen);
347            assert_eq!(term.text_font(), Font::Screen);
348            term.set_text_font(tf);
349            assert_eq!(term.text_font(), Font::Courier);
350
351            let ts = term.text_size();
352            let r = term.h_to_row(100);
353            let c = term.w_to_col(100);
354            term.append(&format!(
355                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
356            ));
357            assert_eq!(ts, 14);
358            term.set_text_size(30);
359            assert_eq!(term.text_size(), 30);
360            term.append(&format!(
361                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
362                term.text_size(),
363                term.h_to_row(100),
364                term.w_to_col(100)
365            ));
366            term.set_text_size(ts);
367            assert_eq!(term.text_size(), ts);
368            term.append(&format!(
369                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
370                term.text_size(),
371                term.h_to_row(100),
372                term.w_to_col(100)
373            ));
374
375            // Keyboard handler
376            term.handle({
377                move |term, e| {
378                    match e {
379                        fltk::enums::Event::KeyDown
380                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
381                        {
382                            // false to let FLTK handle ESC. true to hide ESC
383                            false
384                        }
385
386                        fltk::enums::Event::KeyDown
387                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
388                        {
389                            // We handle control keystroke
390                            let k = fltk::app::event_text();
391                            term.append_utf8(&k);
392                            true
393                        }
394
395                        fltk::enums::Event::KeyDown
396                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
397                        {
398                            // We handle normal printable keystroke
399                            let k = fltk::app::event_text();
400                            term.take_focus().unwrap();
401                            term.append(&k);
402                            true
403                        }
404
405                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
406                        // We can do this, or else ignore them (return false)
407                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
408                        //     term.redraw();
409                        //     true
410                        // }
411                        _ => false, // Let FLTK handle everything else
412                    }
413                }
414            });
415
416            let attr_save = term.text_attrib();
417            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
418            term.append("\nStartup tests complete. Keyboard is live.\n");
419            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
420            term.set_text_attrib(attr_save);
421            assert_eq!(term.text_attrib(), attr_save);
422            term.redraw();
423        }
424    });
425
426    app.run().unwrap();
427}
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 358)
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!(term.text(true), "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");
115
116            // Testing ansi() and set_ansi() methods
117            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
118            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
119            term.set_ansi(false);
120            assert!(!term.ansi());
121            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
122            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
123            term.append_u8(b"Appending u8 array\n");
124            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
125            term.set_ansi(true); // Restore ANSI state
126
127            // Play with the horizontal scrollbar
128            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
129            term.set_hscrollbar_style(ScrollbarStyle::ON);
130            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
131
132            // Test show_unknown() as incidental part of testing append methods
133            term.set_show_unknown(true);
134            assert!(term.show_unknown());
135            term.append_ascii(
136                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
137            );
138            term.set_show_unknown(false);
139            assert!(!term.show_unknown());
140
141            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
142            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
143
144            let r = term.cursor_row();
145            assert_eq!(term.cursor_col(), 0);
146            term.append(&format!("Testing cursor row/col {r}"));
147            assert_eq!(term.cursor_col(), 24);
148            assert_eq!(term.cursor_row(), r);
149
150            // Test cursor color methods
151            assert_eq!(
152                term.cursor_bg_color(),
153                Color::XtermGreen,
154                "Default cursor bg at startup"
155            );
156            assert_eq!(
157                term.cursor_fg_color(),
158                Color::from_hex(0xff_ff_f0),
159                "Default cursor fg at startup"
160            );
161            term.set_cursor_bg_color(Color::Red);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
164            term.set_cursor_fg_color(Color::Blue);
165            assert_eq!(term.cursor_bg_color(), Color::Red);
166            assert_eq!(term.cursor_fg_color(), Color::Blue);
167            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
168            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
169            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
170            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
171
172            // The default display_rows() will derive from the window size
173            let dr = term.display_rows();
174            let height = term.height();
175            assert_eq!(height, term.h());
176            assert!(dr > 20, "Default display_rows at startup");
177            term.resize(term.x(), term.y(), term.w(), height * 2);
178            assert_eq!(term.h(), height * 2);
179            assert_eq!(height * 2, term.h());
180            assert!(term.display_rows() > dr);
181            term.resize(term.x(), term.y(), term.w(), height);
182
183            // The default display_columns() will derive from the window size
184            let dc = term.display_columns();
185            assert!(dc > 80, "Default display_rows at startup");
186            term.set_display_columns(200);
187            assert_eq!(term.display_columns(), 200);
188            term.append("\n         1         2         3         4         5         6         7         8         9");
189            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
190            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
191            term.set_display_columns(90);
192            assert_eq!(term.display_columns(), 90);
193            term.set_display_columns(dc); // Set back to default
194            assert_eq!(term.display_columns(), dc);
195
196            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
197            term.set_history_rows(50);
198            assert_eq!(term.history_rows(), 50);
199            term.set_history_rows(100); // Set back to default
200            assert_eq!(term.history_rows(), 100);
201
202            let hu = term.history_use();
203            term.append(&format!(
204                "history_use = {hu} (it's not clear what this means)\n"
205            ));
206            // assert_eq!(term.history_use(), hu+1);
207
208            term.append(&format!(
209                "margins = b:{} l:{} r:{} t{}\n",
210                term.margin_bottom(),
211                term.margin_left(),
212                term.margin_right(),
213                term.margin_top()
214            ));
215            assert_eq!(term.margin_bottom(), 3);
216            assert_eq!(term.margin_left(), 3);
217            assert_eq!(term.margin_right(), 3);
218            assert_eq!(term.margin_top(), 3);
219
220            term.set_margin_bottom(5);
221            term.set_margin_left(10);
222            term.set_margin_right(15);
223            term.set_margin_top(20);
224            assert_eq!(term.margin_bottom(), 5);
225            assert_eq!(term.margin_left(), 10);
226            assert_eq!(term.margin_right(), 15);
227            assert_eq!(term.margin_top(), 20);
228
229            term.append("Single character: '");
230            term.print_char('X');
231            term.append("', single UTF-8 character: '");
232            term.print_char_utf8('↑');
233            term.append("'\n");
234
235            let rr = term.redraw_rate();
236            assert_eq!(rr, 0.1, "Default redraw rate at startup");
237            term.append(&format!("Redraw rate {rr}\n"));
238            term.set_redraw_rate(1.0);
239            assert_eq!(term.redraw_rate(), 1.0);
240            term.set_redraw_rate(rr);
241            assert_eq!(term.redraw_rate(), rr);
242
243            let rs = term.redraw_style();
244            term.append(&format!("Redraw style {rs:?}\n"));
245            assert_eq!(
246                rs,
247                RedrawStyle::RateLimited,
248                "Default redraw style at startup"
249            );
250            term.set_redraw_style(RedrawStyle::NoRedraw);
251            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
252            term.set_redraw_style(rs);
253            assert_eq!(term.redraw_style(), rs);
254
255            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
256            assert_eq!(
257                RedrawStyle::NoRedraw.bits(),
258                0x0000,
259                "RedrawStyle enum values have been reassigned"
260            );
261            assert_eq!(
262                RedrawStyle::RateLimited.bits(),
263                0x0001,
264                "RedrawStyle enum values have been reassigned"
265            );
266            assert_eq!(
267                RedrawStyle::PerWrite.bits(),
268                0x0002,
269                "RedrawStyle enum values have been reassigned"
270            );
271
272            let sb = term.scrollbar();
273            let hsb = term.hscrollbar();
274            // Both vertical and horizontal scrollbars are at zero
275            assert_eq!(sb.value(), 0.0);
276            assert_eq!(hsb.value(), 0.0);
277            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
278
279            term.append(&format!(
280                "Scrollbar actual size {}\n",
281                term.scrollbar_actual_size()
282            ));
283            assert_eq!(term.scrollbar_actual_size(), 16);
284            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
285            assert_eq!(
286                term.scrollbar_size(),
287                0,
288                "Default scrollbar size at startup"
289            );
290            term.set_scrollbar_size(40);
291            assert_eq!(term.scrollbar_size(), 40);
292            assert_eq!(term.scrollbar_actual_size(), 40);
293            term.append(&format!(
294                "Scrollbar actual size {}\n",
295                term.scrollbar_actual_size()
296            ));
297            term.set_scrollbar_size(0); // Restore default
298            assert_eq!(term.scrollbar_size(), 0);
299            assert_eq!(term.scrollbar_actual_size(), 16);
300
301            let sfc = term.selection_fg_color();
302            let sbc = term.selection_bg_color();
303            assert_eq!(sfc, Color::Black);
304            assert_eq!(sbc, Color::White);
305            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
306            term.set_selection_fg_color(Color::Green);
307            term.set_selection_bg_color(Color::DarkBlue);
308            assert_eq!(term.selection_fg_color(), Color::Green);
309            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
310            term.set_selection_fg_color(sfc);
311            term.set_selection_bg_color(sbc);
312            assert_eq!(term.selection_fg_color(), Color::Black);
313            assert_eq!(term.selection_bg_color(), Color::White);
314
315            let tfcd = term.text_fg_color_default();
316            let tbcd = term.text_bg_color_default();
317            assert_eq!(tfcd, Color::XtermWhite);
318            assert_eq!(tbcd, Color::TransparentBg);
319            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
320            term.set_text_fg_color_default(Color::Green);
321            term.set_text_bg_color_default(Color::DarkBlue);
322            assert_eq!(term.text_fg_color_default(), Color::Green);
323            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
324            term.set_text_fg_color_default(tfcd);
325            term.set_text_bg_color_default(tbcd);
326            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
327            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
328
329            let tfc = term.text_fg_color();
330            let tbc = term.text_bg_color();
331            assert_eq!(tfc, Color::XtermWhite);
332            assert_eq!(tbc, Color::TransparentBg);
333            term.append(&format!("Text colors: {sfc} {sbc}\n"));
334            term.set_text_fg_color(Color::Green);
335            term.set_text_bg_color(Color::DarkBlue);
336            assert_eq!(term.text_fg_color(), Color::Green);
337            assert_eq!(term.text_bg_color(), Color::DarkBlue);
338            term.set_text_fg_color(tfc);
339            term.set_text_bg_color(tbc);
340            assert_eq!(term.text_fg_color(), Color::XtermWhite);
341            assert_eq!(term.text_bg_color(), Color::TransparentBg);
342
343            let tf = term.text_font();
344            term.append(&format!("Text font: {tf:?}\n"));
345            assert_eq!(tf, Font::Courier);
346            term.set_text_font(Font::Screen);
347            assert_eq!(term.text_font(), Font::Screen);
348            term.set_text_font(tf);
349            assert_eq!(term.text_font(), Font::Courier);
350
351            let ts = term.text_size();
352            let r = term.h_to_row(100);
353            let c = term.w_to_col(100);
354            term.append(&format!(
355                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
356            ));
357            assert_eq!(ts, 14);
358            term.set_text_size(30);
359            assert_eq!(term.text_size(), 30);
360            term.append(&format!(
361                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
362                term.text_size(),
363                term.h_to_row(100),
364                term.w_to_col(100)
365            ));
366            term.set_text_size(ts);
367            assert_eq!(term.text_size(), ts);
368            term.append(&format!(
369                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
370                term.text_size(),
371                term.h_to_row(100),
372                term.w_to_col(100)
373            ));
374
375            // Keyboard handler
376            term.handle({
377                move |term, e| {
378                    match e {
379                        fltk::enums::Event::KeyDown
380                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
381                        {
382                            // false to let FLTK handle ESC. true to hide ESC
383                            false
384                        }
385
386                        fltk::enums::Event::KeyDown
387                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
388                        {
389                            // We handle control keystroke
390                            let k = fltk::app::event_text();
391                            term.append_utf8(&k);
392                            true
393                        }
394
395                        fltk::enums::Event::KeyDown
396                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
397                        {
398                            // We handle normal printable keystroke
399                            let k = fltk::app::event_text();
400                            term.take_focus().unwrap();
401                            term.append(&k);
402                            true
403                        }
404
405                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
406                        // We can do this, or else ignore them (return false)
407                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
408                        //     term.redraw();
409                        //     true
410                        // }
411                        _ => false, // Let FLTK handle everything else
412                    }
413                }
414            });
415
416            let attr_save = term.text_attrib();
417            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
418            term.append("\nStartup tests complete. Keyboard is live.\n");
419            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
420            term.set_text_attrib(attr_save);
421            assert_eq!(term.text_attrib(), attr_save);
422            term.redraw();
423        }
424    });
425
426    app.run().unwrap();
427}
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!(term.text(true), "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");
115
116            // Testing ansi() and set_ansi() methods
117            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
118            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
119            term.set_ansi(false);
120            assert!(!term.ansi());
121            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
122            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
123            term.append_u8(b"Appending u8 array\n");
124            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
125            term.set_ansi(true); // Restore ANSI state
126
127            // Play with the horizontal scrollbar
128            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
129            term.set_hscrollbar_style(ScrollbarStyle::ON);
130            assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
131
132            // Test show_unknown() as incidental part of testing append methods
133            term.set_show_unknown(true);
134            assert!(term.show_unknown());
135            term.append_ascii(
136                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
137            );
138            term.set_show_unknown(false);
139            assert!(!term.show_unknown());
140
141            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
142            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
143
144            let r = term.cursor_row();
145            assert_eq!(term.cursor_col(), 0);
146            term.append(&format!("Testing cursor row/col {r}"));
147            assert_eq!(term.cursor_col(), 24);
148            assert_eq!(term.cursor_row(), r);
149
150            // Test cursor color methods
151            assert_eq!(
152                term.cursor_bg_color(),
153                Color::XtermGreen,
154                "Default cursor bg at startup"
155            );
156            assert_eq!(
157                term.cursor_fg_color(),
158                Color::from_hex(0xff_ff_f0),
159                "Default cursor fg at startup"
160            );
161            term.set_cursor_bg_color(Color::Red);
162            assert_eq!(term.cursor_bg_color(), Color::Red);
163            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
164            term.set_cursor_fg_color(Color::Blue);
165            assert_eq!(term.cursor_bg_color(), Color::Red);
166            assert_eq!(term.cursor_fg_color(), Color::Blue);
167            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
168            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
169            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
170            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
171
172            // The default display_rows() will derive from the window size
173            let dr = term.display_rows();
174            let height = term.height();
175            assert_eq!(height, term.h());
176            assert!(dr > 20, "Default display_rows at startup");
177            term.resize(term.x(), term.y(), term.w(), height * 2);
178            assert_eq!(term.h(), height * 2);
179            assert_eq!(height * 2, term.h());
180            assert!(term.display_rows() > dr);
181            term.resize(term.x(), term.y(), term.w(), height);
182
183            // The default display_columns() will derive from the window size
184            let dc = term.display_columns();
185            assert!(dc > 80, "Default display_rows at startup");
186            term.set_display_columns(200);
187            assert_eq!(term.display_columns(), 200);
188            term.append("\n         1         2         3         4         5         6         7         8         9");
189            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
190            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
191            term.set_display_columns(90);
192            assert_eq!(term.display_columns(), 90);
193            term.set_display_columns(dc); // Set back to default
194            assert_eq!(term.display_columns(), dc);
195
196            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
197            term.set_history_rows(50);
198            assert_eq!(term.history_rows(), 50);
199            term.set_history_rows(100); // Set back to default
200            assert_eq!(term.history_rows(), 100);
201
202            let hu = term.history_use();
203            term.append(&format!(
204                "history_use = {hu} (it's not clear what this means)\n"
205            ));
206            // assert_eq!(term.history_use(), hu+1);
207
208            term.append(&format!(
209                "margins = b:{} l:{} r:{} t{}\n",
210                term.margin_bottom(),
211                term.margin_left(),
212                term.margin_right(),
213                term.margin_top()
214            ));
215            assert_eq!(term.margin_bottom(), 3);
216            assert_eq!(term.margin_left(), 3);
217            assert_eq!(term.margin_right(), 3);
218            assert_eq!(term.margin_top(), 3);
219
220            term.set_margin_bottom(5);
221            term.set_margin_left(10);
222            term.set_margin_right(15);
223            term.set_margin_top(20);
224            assert_eq!(term.margin_bottom(), 5);
225            assert_eq!(term.margin_left(), 10);
226            assert_eq!(term.margin_right(), 15);
227            assert_eq!(term.margin_top(), 20);
228
229            term.append("Single character: '");
230            term.print_char('X');
231            term.append("', single UTF-8 character: '");
232            term.print_char_utf8('↑');
233            term.append("'\n");
234
235            let rr = term.redraw_rate();
236            assert_eq!(rr, 0.1, "Default redraw rate at startup");
237            term.append(&format!("Redraw rate {rr}\n"));
238            term.set_redraw_rate(1.0);
239            assert_eq!(term.redraw_rate(), 1.0);
240            term.set_redraw_rate(rr);
241            assert_eq!(term.redraw_rate(), rr);
242
243            let rs = term.redraw_style();
244            term.append(&format!("Redraw style {rs:?}\n"));
245            assert_eq!(
246                rs,
247                RedrawStyle::RateLimited,
248                "Default redraw style at startup"
249            );
250            term.set_redraw_style(RedrawStyle::NoRedraw);
251            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
252            term.set_redraw_style(rs);
253            assert_eq!(term.redraw_style(), rs);
254
255            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
256            assert_eq!(
257                RedrawStyle::NoRedraw.bits(),
258                0x0000,
259                "RedrawStyle enum values have been reassigned"
260            );
261            assert_eq!(
262                RedrawStyle::RateLimited.bits(),
263                0x0001,
264                "RedrawStyle enum values have been reassigned"
265            );
266            assert_eq!(
267                RedrawStyle::PerWrite.bits(),
268                0x0002,
269                "RedrawStyle enum values have been reassigned"
270            );
271
272            let sb = term.scrollbar();
273            let hsb = term.hscrollbar();
274            // Both vertical and horizontal scrollbars are at zero
275            assert_eq!(sb.value(), 0.0);
276            assert_eq!(hsb.value(), 0.0);
277            term.set_hscrollbar_style(ScrollbarStyle::AUTO);
278
279            term.append(&format!(
280                "Scrollbar actual size {}\n",
281                term.scrollbar_actual_size()
282            ));
283            assert_eq!(term.scrollbar_actual_size(), 16);
284            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
285            assert_eq!(
286                term.scrollbar_size(),
287                0,
288                "Default scrollbar size at startup"
289            );
290            term.set_scrollbar_size(40);
291            assert_eq!(term.scrollbar_size(), 40);
292            assert_eq!(term.scrollbar_actual_size(), 40);
293            term.append(&format!(
294                "Scrollbar actual size {}\n",
295                term.scrollbar_actual_size()
296            ));
297            term.set_scrollbar_size(0); // Restore default
298            assert_eq!(term.scrollbar_size(), 0);
299            assert_eq!(term.scrollbar_actual_size(), 16);
300
301            let sfc = term.selection_fg_color();
302            let sbc = term.selection_bg_color();
303            assert_eq!(sfc, Color::Black);
304            assert_eq!(sbc, Color::White);
305            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
306            term.set_selection_fg_color(Color::Green);
307            term.set_selection_bg_color(Color::DarkBlue);
308            assert_eq!(term.selection_fg_color(), Color::Green);
309            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
310            term.set_selection_fg_color(sfc);
311            term.set_selection_bg_color(sbc);
312            assert_eq!(term.selection_fg_color(), Color::Black);
313            assert_eq!(term.selection_bg_color(), Color::White);
314
315            let tfcd = term.text_fg_color_default();
316            let tbcd = term.text_bg_color_default();
317            assert_eq!(tfcd, Color::XtermWhite);
318            assert_eq!(tbcd, Color::TransparentBg);
319            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
320            term.set_text_fg_color_default(Color::Green);
321            term.set_text_bg_color_default(Color::DarkBlue);
322            assert_eq!(term.text_fg_color_default(), Color::Green);
323            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
324            term.set_text_fg_color_default(tfcd);
325            term.set_text_bg_color_default(tbcd);
326            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
327            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
328
329            let tfc = term.text_fg_color();
330            let tbc = term.text_bg_color();
331            assert_eq!(tfc, Color::XtermWhite);
332            assert_eq!(tbc, Color::TransparentBg);
333            term.append(&format!("Text colors: {sfc} {sbc}\n"));
334            term.set_text_fg_color(Color::Green);
335            term.set_text_bg_color(Color::DarkBlue);
336            assert_eq!(term.text_fg_color(), Color::Green);
337            assert_eq!(term.text_bg_color(), Color::DarkBlue);
338            term.set_text_fg_color(tfc);
339            term.set_text_bg_color(tbc);
340            assert_eq!(term.text_fg_color(), Color::XtermWhite);
341            assert_eq!(term.text_bg_color(), Color::TransparentBg);
342
343            let tf = term.text_font();
344            term.append(&format!("Text font: {tf:?}\n"));
345            assert_eq!(tf, Font::Courier);
346            term.set_text_font(Font::Screen);
347            assert_eq!(term.text_font(), Font::Screen);
348            term.set_text_font(tf);
349            assert_eq!(term.text_font(), Font::Courier);
350
351            let ts = term.text_size();
352            let r = term.h_to_row(100);
353            let c = term.w_to_col(100);
354            term.append(&format!(
355                "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
356            ));
357            assert_eq!(ts, 14);
358            term.set_text_size(30);
359            assert_eq!(term.text_size(), 30);
360            term.append(&format!(
361                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
362                term.text_size(),
363                term.h_to_row(100),
364                term.w_to_col(100)
365            ));
366            term.set_text_size(ts);
367            assert_eq!(term.text_size(), ts);
368            term.append(&format!(
369                "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
370                term.text_size(),
371                term.h_to_row(100),
372                term.w_to_col(100)
373            ));
374
375            // Keyboard handler
376            term.handle({
377                move |term, e| {
378                    match e {
379                        fltk::enums::Event::KeyDown
380                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
381                        {
382                            // false to let FLTK handle ESC. true to hide ESC
383                            false
384                        }
385
386                        fltk::enums::Event::KeyDown
387                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
388                        {
389                            // We handle control keystroke
390                            let k = fltk::app::event_text();
391                            term.append_utf8(&k);
392                            true
393                        }
394
395                        fltk::enums::Event::KeyDown
396                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
397                        {
398                            // We handle normal printable keystroke
399                            let k = fltk::app::event_text();
400                            term.take_focus().unwrap();
401                            term.append(&k);
402                            true
403                        }
404
405                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
406                        // We can do this, or else ignore them (return false)
407                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
408                        //     term.redraw();
409                        //     true
410                        // }
411                        _ => false, // Let FLTK handle everything else
412                    }
413                }
414            });
415
416            let attr_save = term.text_attrib();
417            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
418            term.append("\nStartup tests complete. Keyboard is live.\n");
419            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
420            term.set_text_attrib(attr_save);
421            assert_eq!(term.text_attrib(), attr_save);
422            term.redraw();
423        }
424    });
425
426    app.run().unwrap();
427}
Source

pub fn selection_text(&self) -> String

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

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

pub fn disp_erow(&self) -> i32

Return the ending row# in the display area.

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

pub fn disp_srow(&self) -> i32

Return the starting row# in the display area.

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

pub fn hist_erow(&self) -> i32

Return the ending row# of the scrollback history.

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

pub fn hist_srow(&self) -> i32

Return the starting row# of the scrollback history.

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

pub fn ring_rows(&self) -> i32

Return the number of rows in the ring buffer.

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

pub fn u8c_cursor(&self) -> Utf8Char

Return the Utf8Char for character under cursor.

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

fn default_fill() -> Self

Constructs a widget with the size of its parent
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 center_x<W: WidgetExt>(self, w: &W) -> Self

Initialize center of another widget

Source§

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

Initialize center of another widget

Source§

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

Initialize to a 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_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§

fn set_pos(&mut self, x: i32, y: i32)

Set to position x, y
Source§

fn set_size(&mut self, width: i32, height: i32)

Set to dimensions width and height
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 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 width(&self) -> i32

Returns the width of the widget
Source§

fn height(&self) -> i32

Returns the height 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) -> 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_trigger(&mut self, trigger: CallbackTrigger)

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

fn trigger(&self) -> CallbackTrigger

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§

unsafe fn image_mut(&self) -> Option<&mut Image>

Get a reference type of the widget’s image Read more
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§

unsafe fn deimage_mut(&self) -> Option<&mut Image>

Get a reference type of the widget’s deactivated image Read more
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 into_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 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