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
impl Terminal
Sourcepub fn ansi(&self) -> bool
pub fn ansi(&self) -> bool
Returns whether the terminal is in ANSI mode.
Examples found in repository?
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}
Sourcepub fn set_ansi(&mut self, arg1: bool)
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?
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}
Sourcepub fn append(&mut self, s: &str)
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?
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}
Sourcepub fn append_u8(&mut self, s: &[u8])
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?
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}
Sourcepub fn append_ascii(&mut self, s: &str)
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?
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}
Sourcepub fn append_utf8(&mut self, s: &str)
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?
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}
Sourcepub fn append_utf8_u8(&mut self, s: &[u8])
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?
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}
Sourcepub fn clear(&mut self)
pub fn clear(&mut self)
Clears the screen to the current textbgcolor()
, and homes the cursor.
Examples found in repository?
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}
Sourcepub fn clear_mouse_selection(&mut self)
pub fn clear_mouse_selection(&mut self)
Clear any current mouse selection.
Examples found in repository?
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}
Sourcepub fn clear_to_color(&mut self, val: Color)
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?
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}
Sourcepub fn clear_screen(&mut self, arg1: bool)
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
.
Sourcepub fn clear_screen_home(&mut self, arg1: bool)
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?
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}
Sourcepub fn clear_history(&mut self)
pub fn clear_history(&mut self)
Clears the scroll history buffer and adjusts scrollbar, forcing it to redraw()
Examples found in repository?
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}
Sourcepub fn color(&self) -> Color
pub fn color(&self) -> Color
Get the background color for the terminal’s Fl_Group::box().
Examples found in repository?
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}
Sourcepub fn set_color(&mut self, color: Color)
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?
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}
Sourcepub fn cursor_col(&self) -> i32
pub fn cursor_col(&self) -> i32
Return the cursor’s current column position on the screen.
Examples found in repository?
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}
Sourcepub fn set_cursor_col(&mut self, val: i32)
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?
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}
Sourcepub fn cursor_row(&self) -> i32
pub fn cursor_row(&self) -> i32
Return the cursor’s current row position on the screen.
Examples found in repository?
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}
Sourcepub fn set_cursor_row(&mut self, val: i32)
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?
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}
Sourcepub fn cursor_up(&mut self, count: i32, do_scroll: bool)
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?
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}
Sourcepub fn cursor_down(&mut self, count: i32, do_scroll: bool)
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?
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}
Sourcepub fn cursor_left(&mut self, count: i32)
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?
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}
Sourcepub fn cursor_right(&mut self, count: i32, do_scroll: bool)
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?
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}
Sourcepub fn scroll(&mut self, count: i32)
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?
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}
Sourcepub fn clear_eol(&mut self)
pub fn clear_eol(&mut self)
Clear from cursor to End Of Line (EOL), like “<ESC>[K
”.
Examples found in repository?
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}
Sourcepub fn clear_cur_line(&mut self)
pub fn clear_cur_line(&mut self)
Clear entire line cursor is currently on.
Examples found in repository?
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}
Sourcepub fn clear_line(&mut self, drow: i32)
pub fn clear_line(&mut self, drow: i32)
Clear entire line for specified row.
Examples found in repository?
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}
Sourcepub fn clear_sol(&mut self)
pub fn clear_sol(&mut self)
Clear from cursor to Start Of Line (SOL), like “<ESC>[1K
”.
Examples found in repository?
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}
Sourcepub fn insert_char(&mut self, c: char, rep: i32)
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?
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}
Sourcepub fn insert_char_eol(&mut self, c: char, drow: i32, dcol: i32, rep: i32)
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?
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}
Sourcepub fn insert_rows(&mut self, count: i32)
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?
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}
Sourcepub fn delete_chars(&mut self, drow: i32, dcol: i32, count: i32)
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?
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}
Sourcepub fn delete_cur_chars(&mut self, count: i32)
pub fn delete_cur_chars(&mut self, count: i32)
Delete char(s) at cursor position for count
times.
Examples found in repository?
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}
Sourcepub fn delete_rows(&mut self, count: i32)
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?
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}
Sourcepub fn cursor_bg_color(&self) -> Color
pub fn cursor_bg_color(&self) -> Color
Get the cursor’s background color used for the cursor itself.
Examples found in repository?
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}
Sourcepub fn set_cursor_bg_color(&mut self, color: Color)
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?
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}
Sourcepub fn cursor_fg_color(&self) -> Color
pub fn cursor_fg_color(&self) -> Color
Get the cursor’s foreground color used for the cursor itself.
Examples found in repository?
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}
Sourcepub fn set_cursor_fg_color(&mut self, color: Color)
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?
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}
Sourcepub fn get_selection(&self) -> Option<[i32; 4]>
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?
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}
Sourcepub fn cursor_home(&mut self)
pub fn cursor_home(&mut self)
Move cursor to the home position (top/left).
Examples found in repository?
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}
Sourcepub fn display_columns(&self) -> i32
pub fn display_columns(&self) -> i32
Return terminal’s display width in columns of text characters.
Examples found in repository?
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}
Sourcepub fn set_display_columns(&mut self, val: i32)
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?
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}
Sourcepub fn display_rows(&self) -> i32
pub fn display_rows(&self) -> i32
Return terminal’s display height in lines of text.
Examples found in repository?
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}
Sourcepub fn set_history_rows(&mut self, arg1: i32)
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?
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}
Sourcepub fn history_rows(&self) -> i32
pub fn history_rows(&self) -> i32
Gets the terminal’s scrollback history buffer size in lines of text (rows).
Examples found in repository?
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}
Sourcepub fn history_use(&self) -> i32
pub fn history_use(&self) -> i32
Returns how many lines are “in use” by the screen history buffer.
Examples found in repository?
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}
Sourcepub fn set_margin_bottom(&mut self, arg1: i32)
pub fn set_margin_bottom(&mut self, arg1: i32)
Set the bottom margin
Examples found in repository?
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}
Sourcepub fn margin_bottom(&self) -> i32
pub fn margin_bottom(&self) -> i32
Return the bottom margin
Examples found in repository?
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}
Sourcepub fn set_margin_left(&mut self, arg1: i32)
pub fn set_margin_left(&mut self, arg1: i32)
Set the left margin
Examples found in repository?
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}
Sourcepub fn margin_left(&self) -> i32
pub fn margin_left(&self) -> i32
Return the left margin
Examples found in repository?
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}
Sourcepub fn set_margin_right(&mut self, arg1: i32)
pub fn set_margin_right(&mut self, arg1: i32)
Set the right margin
Examples found in repository?
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}
Sourcepub fn margin_right(&self) -> i32
pub fn margin_right(&self) -> i32
Return the right margin
Examples found in repository?
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}
Sourcepub fn set_margin_top(&mut self, arg1: i32)
pub fn set_margin_top(&mut self, arg1: i32)
Set the top margin
Examples found in repository?
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}
Sourcepub fn margin_top(&self) -> i32
pub fn margin_top(&self) -> i32
Return the top margin
Examples found in repository?
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}
Sourcepub fn h_to_row(&self, pix: i32) -> i32
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?
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}
Sourcepub fn w_to_col(&self, pix: i32) -> i32
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?
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}
Sourcepub fn set_output_translate(&mut self, val: OutFlags)
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?
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}
Sourcepub fn output_translate(&self) -> OutFlags
pub fn output_translate(&self) -> OutFlags
Return the current combined output translation flags.
Examples found in repository?
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}
Sourcepub fn print_char(&mut self, c: char)
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?
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}
Sourcepub fn print_char_utf8(&mut self, c: char)
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?
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}
Sourcepub fn plot_char(&mut self, c: char, row: i32, col: i32)
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?
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}
Sourcepub fn plot_char_utf8(&mut self, c: char, drow: i32, dcol: i32)
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?
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}
Sourcepub fn set_redraw_rate(&mut self, set: f32)
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?
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}
Sourcepub fn redraw_rate(&self) -> f32
pub fn redraw_rate(&self) -> f32
Get max rate redraw speed in floating point seconds.
Examples found in repository?
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}
Sourcepub fn set_redraw_style(&mut self, set: RedrawStyle)
pub fn set_redraw_style(&mut self, set: RedrawStyle)
Set how Terminal manages screen redrawing.
Examples found in repository?
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}
Sourcepub fn redraw_style(&self) -> RedrawStyle
pub fn redraw_style(&self) -> RedrawStyle
Get the redraw style.
Examples found in repository?
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}
Sourcepub fn reset_terminal(&mut self)
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?
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}
Sourcepub fn scrollbar_actual_size(&self) -> i32
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?
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}
Sourcepub fn scrollbar_size(&self) -> i32
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?
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}
Sourcepub fn set_scrollbar_size(&mut self, val: i32)
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?
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}
Sourcepub fn selection_bg_color(&self) -> Color
pub fn selection_bg_color(&self) -> Color
Get mouse selection background color.
Examples found in repository?
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}
Sourcepub fn scrollbar(&self) -> Scrollbar
pub fn scrollbar(&self) -> Scrollbar
Returns the vertical scrollbar
Examples found in repository?
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}
Sourcepub fn hscrollbar(&self) -> Scrollbar
pub fn hscrollbar(&self) -> Scrollbar
Returns the horizontal scrollbar
Examples found in repository?
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}
Sourcepub fn hscrollbar_style(&self) -> ScrollbarStyle
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?
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}
Sourcepub fn set_hscrollbar_style(&mut self, val: ScrollbarStyle)
pub fn set_hscrollbar_style(&mut self, val: ScrollbarStyle)
Set the scrollbar behavior style.
This determines when the scrollbar is visible.
ScrollbarStyle enum | Description |
---|---|
ON | Scrollbar is always displayed. |
OFF | Scrollbar is never displayed. |
AUTO | Scrollbar 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?
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}
Sourcepub fn set_selection_bg_color(&mut self, color: Color)
pub fn set_selection_bg_color(&mut self, color: Color)
Set mouse selection background color.
Examples found in repository?
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}
Sourcepub fn selection_fg_color(&self) -> Color
pub fn selection_fg_color(&self) -> Color
Get mouse selection foreground color.
Examples found in repository?
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}
Sourcepub fn set_selection_fg_color(&mut self, color: Color)
pub fn set_selection_fg_color(&mut self, color: Color)
Set mouse selection foreground color.
Examples found in repository?
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}
Sourcepub fn show_unknown(&self) -> bool
pub fn show_unknown(&self) -> bool
Return the “show unknown” flag. if true, show unknown chars as ‘¿’
Examples found in repository?
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}
Sourcepub fn set_show_unknown(&mut self, arg1: bool)
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?
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}
Sourcepub fn text_attrib(&self) -> Attrib
pub fn text_attrib(&self) -> Attrib
Return the text attribute bits (underline, inverse, etc) for subsequent appends.
Examples found in repository?
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}
Sourcepub fn set_text_attrib(&mut self, arg1: Attrib)
pub fn set_text_attrib(&mut self, arg1: Attrib)
Set text attribute bits (underline, inverse, etc) for subsequent appends.
Examples found in repository?
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}
Sourcepub fn set_text_bg_color(&mut self, color: Color)
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?
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}
Sourcepub fn text_bg_color(&self) -> Color
pub fn text_bg_color(&self) -> Color
Get the text background color.
Examples found in repository?
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}
Sourcepub fn set_text_bg_color_default(&mut self, color: Color)
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?
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}
Sourcepub fn text_bg_color_default(&self) -> Color
pub fn text_bg_color_default(&self) -> Color
Return the default text background color.
Examples found in repository?
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}
Sourcepub fn set_text_bg_color_xterm(&mut self, color: XtermColor)
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?
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}
Sourcepub fn set_text_color(&mut self, color: Color)
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.
Sourcepub fn set_text_fg_color(&mut self, color: Color)
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?
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}
Sourcepub fn text_fg_color(&self) -> Color
pub fn text_fg_color(&self) -> Color
Get the text foreground color.
Examples found in repository?
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}
Sourcepub fn set_text_fg_color_default(&mut self, color: Color)
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?
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}
Sourcepub fn text_fg_color_default(&self) -> Color
pub fn text_fg_color_default(&self) -> Color
Return the default text foreground color.
Examples found in repository?
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}
Sourcepub fn set_text_fg_color_xterm(&mut self, color: XtermColor)
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?
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}
Sourcepub fn text_font(&self) -> Font
pub fn text_font(&self) -> Font
Get the text font
Examples found in repository?
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}
Sourcepub fn set_text_font(&mut self, font: Font)
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?
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}
Sourcepub fn text_size(&self) -> i32
pub fn text_size(&self) -> i32
Return text font size used to draw all text in the terminal.
Examples found in repository?
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}
Sourcepub fn set_text_size(&mut self, val: i32)
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?
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}
Sourcepub fn text(&self, lines_below_cursor: bool) -> String
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?
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}
Sourcepub fn selection_text(&self) -> String
pub fn selection_text(&self) -> String
Return text selection (for copy()/paste() operations)
Examples found in repository?
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}
Sourcepub fn selection_text_len(&self) -> i32
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?
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}
Sourcepub fn disp_erow(&self) -> i32
pub fn disp_erow(&self) -> i32
Return the ending row# in the display area.
Examples found in repository?
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}
Sourcepub fn disp_srow(&self) -> i32
pub fn disp_srow(&self) -> i32
Return the starting row# in the display area.
Examples found in repository?
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}
Sourcepub fn hist_erow(&self) -> i32
pub fn hist_erow(&self) -> i32
Return the ending row# of the scrollback history.
Examples found in repository?
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}
Sourcepub fn hist_srow(&self) -> i32
pub fn hist_srow(&self) -> i32
Return the starting row# of the scrollback history.
Examples found in repository?
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}
Sourcepub fn hist_use_srow(&self) -> i32
pub fn hist_use_srow(&self) -> i32
Return the starting row of the "in use" scrollback history.
Sourcepub fn is_inside_selection(&self, row: i32, col: i32) -> bool
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
Sourcepub fn is_selection(&self) -> bool
pub fn is_selection(&self) -> bool
Returns true if there’s a mouse selection.
Sourcepub fn offset(&self) -> i32
pub fn offset(&self) -> i32
Returns the current offset into the ring buffer.
Examples found in repository?
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}
Sourcepub fn ring_erow(&self) -> i32
pub fn ring_erow(&self) -> i32
Return the ending row# in the ring buffer (Always ring_rows()-1)
Examples found in repository?
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}
Sourcepub fn ring_srow(&self) -> i32
pub fn ring_srow(&self) -> i32
Return the starting row# in the ring buffer (Always 0)
Examples found in repository?
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}
Sourcepub fn ring_rows(&self) -> i32
pub fn ring_rows(&self) -> i32
Return the number of rows in the ring buffer.
Examples found in repository?
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}
Sourcepub fn u8c_cursor(&self) -> Utf8Char
pub fn u8c_cursor(&self) -> Utf8Char
Return the Utf8Char for character under cursor.
Examples found in repository?
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}
Sourcepub fn u8c_disp_row(&self, drow: i32) -> BuffRow<'_>
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?
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}
Sourcepub fn u8c_hist_row(&self, hrow: i32) -> BuffRow<'_>
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
Sourcepub fn u8c_hist_use_row(&self, hurow: i32) -> BuffRow<'_>
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
Sourcepub fn u8c_ring_row(&self, grow: i32) -> BuffRow<'_>
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 GroupExt for Terminal
impl GroupExt for Terminal
Source§fn find<W: WidgetExt>(&self, widget: &W) -> i32
fn find<W: WidgetExt>(&self, widget: &W) -> i32
Source§fn insert<W: WidgetExt>(&mut self, widget: &W, index: i32)
fn insert<W: WidgetExt>(&mut self, widget: &W, index: i32)
Source§fn remove<W: WidgetExt>(&mut self, widget: &W)
fn remove<W: WidgetExt>(&mut self, widget: &W)
Source§fn remove_by_index(&mut self, idx: i32)
fn remove_by_index(&mut self, idx: i32)
Source§fn resizable<W: WidgetExt>(&self, widget: &W)
fn resizable<W: WidgetExt>(&self, widget: &W)
Source§fn make_resizable(&mut self, val: bool)
fn make_resizable(&mut self, val: bool)
Source§fn add_resizable<W: WidgetExt>(&mut self, widget: &W)
fn add_resizable<W: WidgetExt>(&mut self, widget: &W)
Source§fn set_clip_children(&mut self, flag: bool)
fn set_clip_children(&mut self, flag: bool)
Source§fn clip_children(&self) -> bool
fn clip_children(&self) -> bool
clip_children
is setSource§fn draw_child<W: WidgetExt>(&self, w: &mut W)
fn draw_child<W: WidgetExt>(&self, w: &mut W)
WidgetBase::draw
methodSource§fn update_child<W: WidgetExt>(&self, w: &mut W)
fn update_child<W: WidgetExt>(&self, w: &mut W)
WidgetBase::draw
methodSource§fn draw_outside_label<W: WidgetExt>(&self, w: &mut W)
fn draw_outside_label<W: WidgetExt>(&self, w: &mut W)
WidgetBase::draw
methodSource§fn draw_children(&mut self)
fn draw_children(&mut self)
WidgetBase::draw
methodSource§fn init_sizes(&mut self)
fn init_sizes(&mut self)
Source§fn bounds(&self) -> Vec<(i32, i32, i32, i32)>
fn bounds(&self) -> Vec<(i32, i32, i32, i32)>
Source§unsafe fn into_group(&self) -> Group
unsafe fn into_group(&self) -> Group
Source§impl IntoIterator for Terminal
impl IntoIterator for Terminal
Source§impl WidgetBase for Terminal
impl WidgetBase for Terminal
Source§fn new<'a, T: Into<Option<&'a str>>>(
x: i32,
y: i32,
width: i32,
height: i32,
title: T,
) -> Terminal
fn new<'a, T: Into<Option<&'a str>>>( x: i32, y: i32, width: i32, height: i32, title: T, ) -> Terminal
Source§fn default_fill() -> Self
fn default_fill() -> Self
Source§unsafe fn from_widget_ptr(ptr: *mut Fl_Widget) -> Self
unsafe fn from_widget_ptr(ptr: *mut Fl_Widget) -> Self
Source§unsafe fn from_widget<W: WidgetExt>(w: W) -> Self
unsafe fn from_widget<W: WidgetExt>(w: W) -> Self
Source§fn handle<F: FnMut(&mut Self, Event) -> bool + 'static>(&mut self, cb: F)
fn handle<F: FnMut(&mut Self, Event) -> bool + 'static>(&mut self, cb: F)
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 hereSource§fn draw<F: FnMut(&mut Self) + 'static>(&mut self, cb: F)
fn draw<F: FnMut(&mut Self) + 'static>(&mut self, cb: F)
WidgetBase::draw
actually calls drawing functionsSource§fn resize_callback<F: FnMut(&mut Self, i32, i32, i32, i32) + 'static>(
&mut self,
cb: F,
)
fn resize_callback<F: FnMut(&mut Self, i32, i32, i32, i32) + 'static>( &mut self, cb: F, )
Source§unsafe fn assume_derived(&mut self)
unsafe fn assume_derived(&mut self)
Source§impl WidgetExt for Terminal
impl WidgetExt for Terminal
Source§fn with_label(self, title: &str) -> Self
fn with_label(self, title: &str) -> Self
Source§fn with_align(self, align: Align) -> Self
fn with_align(self, align: Align) -> Self
Source§fn with_type<T: WidgetType>(self, typ: T) -> Self
fn with_type<T: WidgetType>(self, typ: T) -> Self
Source§fn below_of<W: WidgetExt>(self, wid: &W, padding: i32) -> Self
fn below_of<W: WidgetExt>(self, wid: &W, padding: i32) -> Self
Source§fn above_of<W: WidgetExt>(self, wid: &W, padding: i32) -> Self
fn above_of<W: WidgetExt>(self, wid: &W, padding: i32) -> Self
Source§fn right_of<W: WidgetExt>(self, wid: &W, padding: i32) -> Self
fn right_of<W: WidgetExt>(self, wid: &W, padding: i32) -> Self
Source§fn left_of<W: WidgetExt>(self, wid: &W, padding: i32) -> Self
fn left_of<W: WidgetExt>(self, wid: &W, padding: i32) -> Self
Source§fn center_of_parent(self) -> Self
fn center_of_parent(self) -> Self
Source§fn size_of_parent(self) -> Self
fn size_of_parent(self) -> Self
Source§fn set_label(&mut self, title: &str)
fn set_label(&mut self, title: &str)
@
sign.
and for the associated formatting.Source§fn measure_label(&self) -> (i32, i32)
fn measure_label(&self) -> (i32, i32)
Source§fn as_widget_ptr(&self) -> *mut Fl_Widget
fn as_widget_ptr(&self) -> *mut Fl_Widget
Fl_Widget
, for internal useSource§fn deactivate(&mut self)
fn deactivate(&mut self)
Source§fn redraw_label(&mut self)
fn redraw_label(&mut self)
Source§fn resize(&mut self, x: i32, y: i32, width: i32, height: i32)
fn resize(&mut self, x: i32, y: i32, width: i32, height: i32)
Source§fn widget_resize(&mut self, x: i32, y: i32, width: i32, height: i32)
fn widget_resize(&mut self, x: i32, y: i32, width: i32, height: i32)
Source§fn set_tooltip(&mut self, txt: &str)
fn set_tooltip(&mut self, txt: &str)
Source§fn label_color(&self) -> Color
fn label_color(&self) -> Color
Source§fn set_label_color(&mut self, color: Color)
fn set_label_color(&mut self, color: Color)
Source§fn label_font(&self) -> Font
fn label_font(&self) -> Font
Source§fn set_label_font(&mut self, font: Font)
fn set_label_font(&mut self, font: Font)
Source§fn label_size(&self) -> i32
fn label_size(&self) -> i32
Source§fn set_label_size(&mut self, sz: i32)
fn set_label_size(&mut self, sz: i32)
Source§fn label_type(&self) -> LabelType
fn label_type(&self) -> LabelType
Source§fn set_label_type(&mut self, typ: LabelType)
fn set_label_type(&mut self, typ: LabelType)
Source§fn set_changed(&mut self)
fn set_changed(&mut self)
Source§fn clear_changed(&mut self)
fn clear_changed(&mut self)
Source§fn set_trigger(&mut self, trigger: CallbackTrigger)
fn set_trigger(&mut self, trigger: CallbackTrigger)
when()
Source§fn trigger(&self) -> CallbackTrigger
fn trigger(&self) -> CallbackTrigger
when()
Source§fn selection_color(&self) -> Color
fn selection_color(&self) -> Color
Source§fn set_selection_color(&mut self, color: Color)
fn set_selection_color(&mut self, color: Color)
Source§fn do_callback(&mut self)
fn do_callback(&mut self)
Source§fn top_window(&self) -> Option<Box<dyn WindowExt>>
fn top_window(&self) -> Option<Box<dyn WindowExt>>
Source§fn takes_events(&self) -> bool
fn takes_events(&self) -> bool
Source§fn set_visible_focus(&mut self)
fn set_visible_focus(&mut self)
Source§fn clear_visible_focus(&mut self)
fn clear_visible_focus(&mut self)
Source§fn visible_focus(&mut self, v: bool)
fn visible_focus(&mut self, v: bool)
Source§fn has_visible_focus(&self) -> bool
fn has_visible_focus(&self) -> bool
Source§fn was_deleted(&self) -> bool
fn was_deleted(&self) -> bool
Source§fn set_damage(&mut self, flag: bool)
fn set_damage(&mut self, flag: bool)
Source§fn damage_type(&self) -> Damage
fn damage_type(&self) -> Damage
Source§fn set_damage_type(&mut self, mask: Damage)
fn set_damage_type(&mut self, mask: Damage)
Source§fn set_damage_area(&mut self, mask: Damage, x: i32, y: i32, w: i32, h: i32)
fn set_damage_area(&mut self, mask: Damage, x: i32, y: i32, w: i32, h: i32)
Source§fn clear_damage(&mut self)
fn clear_damage(&mut self)
Source§fn as_window(&self) -> Option<Box<dyn WindowExt>>
fn as_window(&self) -> Option<Box<dyn WindowExt>>
Source§fn as_group(&self) -> Option<Group>
fn as_group(&self) -> Option<Group>
Source§fn inside<W: WidgetExt>(&self, wid: &W) -> bool
fn inside<W: WidgetExt>(&self, wid: &W) -> bool
Source§fn get_type<T: WidgetType>(&self) -> T
fn get_type<T: WidgetType>(&self) -> T
Source§fn set_type<T: WidgetType>(&mut self, typ: T)
fn set_type<T: WidgetType>(&mut self, typ: T)
Source§fn set_image_scaled<I: ImageExt>(&mut self, image: Option<I>)
fn set_image_scaled<I: ImageExt>(&mut self, image: Option<I>)
Source§unsafe fn image_mut(&self) -> Option<&mut Image>
unsafe fn image_mut(&self) -> Option<&mut Image>
Source§fn set_deimage<I: ImageExt>(&mut self, image: Option<I>)
fn set_deimage<I: ImageExt>(&mut self, image: Option<I>)
Source§fn set_deimage_scaled<I: ImageExt>(&mut self, image: Option<I>)
fn set_deimage_scaled<I: ImageExt>(&mut self, image: Option<I>)
Source§fn deimage(&self) -> Option<Box<dyn ImageExt>>
fn deimage(&self) -> Option<Box<dyn ImageExt>>
Source§unsafe fn deimage_mut(&self) -> Option<&mut Image>
unsafe fn deimage_mut(&self) -> Option<&mut Image>
Source§fn set_callback<F: FnMut(&mut Self) + 'static>(&mut self, cb: F)
fn set_callback<F: FnMut(&mut Self) + 'static>(&mut self, cb: F)
Source§fn emit<T: 'static + Clone + Send + Sync>(&mut self, sender: Sender<T>, msg: T)
fn emit<T: 'static + Clone + Send + Sync>(&mut self, sender: Sender<T>, msg: T)
Source§unsafe fn into_widget<W: WidgetBase>(&self) -> W
unsafe fn into_widget<W: WidgetBase>(&self) -> W
WidgetExt
to some widget type Read moreSource§fn visible_r(&self) -> bool
fn visible_r(&self) -> bool
Source§fn is_same<W: WidgetExt>(&self, other: &W) -> bool
fn is_same<W: WidgetExt>(&self, other: &W) -> bool
Source§fn active_r(&self) -> bool
fn active_r(&self) -> bool
Source§fn handle_event(&mut self, event: Event) -> bool
fn handle_event(&mut self, event: Event) -> bool
Source§fn is_derived(&self) -> bool
fn is_derived(&self) -> bool
Source§fn as_base_widget(&self) -> Widgetwhere
Self: Sized,
fn as_base_widget(&self) -> Widgetwhere
Self: Sized,
WidgetExt
to a Widgetimpl Eq for Terminal
impl Send for Terminal
single-threaded
only.impl Sync for Terminal
single-threaded
only.