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 new<'a, T: Into<Option<&'a str>>>(
x: i32,
y: i32,
width: i32,
height: i32,
title: T,
) -> Terminal
pub fn new<'a, T: Into<Option<&'a str>>>( x: i32, y: i32, width: i32, height: i32, title: T, ) -> Terminal
Creates a new widget, takes an x, y coordinates, as well as a width and height, plus a title
§Arguments
x
- The x coordinate in the screeny
- The y coordinate in the screenwidth
- The width of the widgetheigth
- The height of the widgettitle
- The title or label of the widget
To use dynamic strings use with_label(self, &str)
or set_label(&mut self, &str)
.
labels support special symbols preceded by an @
sign
and for the associated formatting.
Examples found in repository?
20fn main() {
21 let app = fltk::app::App::default();
22
23 // Set panic handler for main thread (will become UI thread)
24 std::panic::set_hook(Box::new({
25 |e| {
26 eprintln!("!!!!PANIC!!!!{:#?}", e);
27 error_box(e.to_string()); // Only works from the UI thread
28 std::process::exit(2);
29 }
30 }));
31
32 let mut main_win = Window::new(
33 2285,
34 180,
35 WIN_WIDTH,
36 WIN_HEIGHT,
37 "FLTK/Terminal Rust wrapper test",
38 );
39 main_win.set_type(WindowType::Double);
40 main_win.make_resizable(true);
41
42 let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44 let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45 term.set_label("term");
46 main_win.resizable(&term);
47 term.set_label_type(LabelType::None);
48
49 let idx = menu_bar.add_choice("Test&1");
50 menu_bar.at(idx).unwrap().set_callback({
51 let mut term1 = term.clone();
52 move |c| mb_test1_cb(c, &mut term1)
53 });
54 menu_bar
55 .at(idx)
56 .unwrap()
57 .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59 let idx = menu_bar.add_choice("Test&2");
60 menu_bar.at(idx).unwrap().set_callback({
61 let mut term1 = term.clone();
62 move |c| mb_test2_cb(c, &mut term1)
63 });
64 menu_bar
65 .at(idx)
66 .unwrap()
67 .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69 let idx = menu_bar.add_choice("Test&3");
70 menu_bar.at(idx).unwrap().set_callback({
71 let mut term1 = term.clone();
72 move |c| mb_test3_cb(c, &mut term1)
73 });
74 menu_bar
75 .at(idx)
76 .unwrap()
77 .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79 let idx = menu_bar.add_choice("Test&4");
80 menu_bar.at(idx).unwrap().set_callback({
81 let mut term1 = term.clone();
82 move |c| mb_test4_cb(c, &mut term1)
83 });
84 menu_bar
85 .at(idx)
86 .unwrap()
87 .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89 let idx = menu_bar.add_choice("Test&5");
90 menu_bar.at(idx).unwrap().set_callback({
91 let mut term1 = term.clone();
92 move |c| mb_test5_cb(c, &mut term1)
93 });
94 menu_bar
95 .at(idx)
96 .unwrap()
97 .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99 menu_bar.end();
100
101 main_win.end();
102 main_win.show();
103
104 // Worker thread that drives the startup tests
105 let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106 let mut term = term.clone();
107 move || {
108 println!("Startup tests\n");
109 term.append("Startup tests\n\n");
110 term.append("<tmp>\n"); // This line will be overwritten later
111
112 term.cursor_up(2, false);
113 assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114 assert_eq!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
Sourcepub fn default_fill() -> Self
pub fn default_fill() -> Self
Constructs a widget with the size of its parent
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
425//--------------------------------------------------------------------------------------
426/// More tests that run when the menu bar Test1 is clicked
427fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
428 term.take_focus().unwrap();
429 term.reset_terminal();
430 term.append("0123456789 0\n");
431 term.append("0123456789 1\n");
432 term.append("0123456789 2\n");
433 term.append("0123456789 3\n");
434 term.append("0123456789 4\n");
435 term.append("0123456789 5\n");
436 term.append("0123456789 6\n");
437 term.append("0123456789 7\n");
438 term.append("0123456789 8\n");
439 term.append("0123456789 9\n");
440 term.append("------------\n");
441
442 term.set_text_fg_color(Color::Green);
443 term.plot_char('A', 0, 0);
444 term.plot_char('B', 1, 1);
445 term.plot_char('C', 2, 2);
446 term.plot_char('D', 3, 3);
447 term.plot_char('E', 4, 4);
448 term.plot_char('F', 5, 5);
449 term.set_text_fg_color(Color::XtermWhite);
450
451 assert_eq!(term.cursor_row(), 11);
452 assert_eq!(term.cursor_col(), 0);
453
454 term.set_text_bg_color(Color::DarkBlue);
455 term.plot_char_utf8('b', 8, 1);
456 term.plot_char_utf8('↑', 9, 1);
457 term.plot_char_utf8('c', 8, 2);
458 term.plot_char_utf8('↑', 9, 2);
459 term.plot_char_utf8('d', 8, 3);
460 term.plot_char_utf8('↑', 9, 3);
461 term.plot_char_utf8('e', 8, 4);
462 term.plot_char_utf8('↑', 9, 4);
463 term.plot_char_utf8('f', 8, 5);
464 term.plot_char_utf8('↑', 9, 5);
465 term.plot_char_utf8('g', 8, 6);
466 term.plot_char_utf8('↑', 9, 6);
467 term.set_text_bg_color(Color::TransparentBg);
468
469 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
470 term.append("Done!\n");
471 term.set_text_attrib(Attrib::Normal);
472}
473
474//--------------------------------------------------------------------------------------
475/// More tests that run when the menu bar button Test2 is clicked
476fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
477 term.take_focus().unwrap();
478 term.reset_terminal();
479
480 for i in 0..50 {
481 term.append(&format!("{i}\n"));
482 }
483 assert_eq!(term.history_rows(), 100);
484
485 term.clear_history();
486 assert_eq!(term.history_use(), 0);
487
488 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
489 term.append("\nDone!\n");
490 term.set_text_attrib(Attrib::Normal);
491}
492
493//--------------------------------------------------------------------------------------
494/// Another set of tests that run when Test3 is clicked
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496 term.take_focus().unwrap();
497 term.reset_terminal();
498 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500 assert_eq!(term.history_use(), 0);
501 term.clear();
502 assert_eq!(term.cursor_row(), 0);
503 assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505 term.append("Test\ntext\na\nb\nc\nd");
506 assert_eq!(term.cursor_row(), 5);
507 let hist = term.history_use();
508 term.clear_screen_home(false);
509 assert_eq!(term.cursor_row(), 0);
510 assert_eq!(term.history_use(), hist); // History not changed
511
512 term.append("Test\ntext\na\nb\nc\nd\ne");
513 assert_eq!(term.cursor_row(), 6);
514 term.clear_screen_home(true);
515 assert_eq!(term.cursor_row(), 0);
516
517 term.append("Test\ntext\na\nb\nc\n");
518 assert_eq!(term.cursor_row(), 5);
519 term.clear_to_color(Color::DarkBlue);
520 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521 assert_eq!(term.text_bg_color(), Color::TransparentBg);
522 assert_eq!(term.cursor_row(), 0);
523
524 // Test cursor_home()
525 term.append("Test\n\n\n\n\n\n\n\n\n\n");
526 assert_eq!(term.cursor_row(), 10);
527 term.cursor_home();
528 assert_eq!(term.cursor_row(), 0);
529
530 // Test the widget color
531 assert_eq!(term.color(), Color::Black); // Default
532 term.set_color(Color::DarkGreen);
533 assert_eq!(term.color(), Color::DarkGreen);
534 term.set_color(Color::Black);
535 assert_eq!(term.color(), Color::Black);
536 term.append(
537 "This should be one line of white text on black, embedded into the top of a blue field.\n",
538 );
539
540 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541 term.set_output_translate(OutFlags::OFF);
542 assert_eq!(term.output_translate(), OutFlags::OFF);
543 term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547 term.append("\nDone!\n");
548 term.set_text_attrib(Attrib::Normal);
549}
550
551//--------------------------------------------------------------------------------------
552/// Another set of tests for the ring-buffer access methods
553/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555 let sel_len = term.selection_text_len();
556 let sel = term.selection_text();
557
558 term.take_focus().unwrap();
559 term.reset_terminal();
560 // Test the Utf8Char primitive
561 let uc = Utf8Char::new(b'Q');
562 let uc1 = uc.text_utf8();
563 assert_eq!(&uc1, b"Q");
564 assert_eq!(&uc.attrib(), &Attrib::Normal);
565 assert_eq!(
566 &uc.charflags(),
567 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568 );
569 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570 assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572 let ring_rows = term.ring_rows();
573
574 term.take_focus().unwrap();
575 term.clear_history();
576 assert_eq!(term.history_use(), 0);
577
578 // Subtract row numbers, modulo `rows`
579 fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580 match a - b {
581 n if n < 0 => n + rows,
582 n => n,
583 }
584 }
585 // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587 assert!(term.disp_srow() >= 0);
588 assert!(term.disp_erow() >= 0);
589 assert!(term.hist_srow() >= 0);
590 assert!(term.hist_erow() >= 0);
591 assert!(term.offset() >= 0);
592 assert!(term.disp_srow() <= ring_rows);
593 assert!(term.disp_erow() <= ring_rows);
594 assert!(term.hist_srow() <= ring_rows);
595 assert!(term.hist_erow() <= ring_rows);
596 assert!(term.offset() <= ring_rows);
597
598 assert_eq!(term.ring_srow(), 0);
599 assert_eq!(term.ring_erow(), ring_rows - 1);
600 assert_eq!(
601 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602 term.display_rows()
603 );
604 assert_eq!(
605 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606 term.history_rows()
607 );
608
609 assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610 assert_eq!(term.ring_srow(), 0);
611
612 /// Local function to read back all rows from the display into a long string.
613 /// Does not include scrollback history.
614 /// Trims trailing blanks on each line
615 fn read_disp(term: &Terminal) -> String {
616 let rows = term.display_rows();
617 let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618 for row in 0..rows {
619 let r = term.u8c_disp_row(row).trim();
620 // Iterate through a row, accumulating [u8]
621 for c in r.iter() {
622 // Note: Sometimes utf-8 length is > 1
623 text.extend_from_slice(c.text_utf8());
624 }
625 text.extend_from_slice(b"\n");
626 }
627 // Return the result as a string
628 std::str::from_utf8(&text).unwrap().to_string()
629 }
630
631 term.clear();
632 term.append("Top line ↑ (up-arrow)");
633 term.set_text_attrib(Attrib::Underline);
634 term.append(" ");
635 term.set_text_attrib(Attrib::Normal);
636 term.append(" \n");
637 let mut text_out = read_disp(term);
638 // Trim trailing empty lines
639 text_out = text_out.trim_end_matches(&"\n").to_string();
640 // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642 assert_eq!(text_out, "Top line ↑ (up-arrow) ");
643 let r = term.u8c_disp_row(0);
644 assert_eq!(r.col(0).text_utf8(), b"T");
645 assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646 assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647 let r = term.u8c_disp_row(1);
648 assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649 assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651 // Clear the screen again, then append test text, then read it back and compare
652 let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656 Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659 term.clear_history();
660 term.clear();
661 let bg_save = term.text_bg_color();
662 let fg_save = term.text_fg_color();
663 term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664 term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665 term.append(test_text);
666 term.set_text_bg_color(bg_save);
667 term.set_text_fg_color(fg_save);
668
669 let mut text_out = read_disp(term);
670 // Trim trailing empty lines
671 text_out = text_out.trim_end_matches(&"\n").to_string();
672 assert_eq!(test_text, text_out);
673
674 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676 assert_eq!(term.ring_srow(), 0);
677 assert_eq!(term.ring_erow(), ring_rows - 1);
678 assert_eq!(
679 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680 term.display_rows()
681 );
682 assert_eq!(
683 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684 term.history_rows()
685 );
686
687 term.append(&format!(
688 "\n\nScreen has {} rows of {} columns.\n",
689 term.display_rows(),
690 term.display_columns()
691 ));
692
693 term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
695
696//--------------------------------------------------------------------------------------
697/// Yet another set of tests for misc cursor functions and other stuff
698/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700 term.take_focus().unwrap();
701
702 // Test the attr_fg_color and attr_bg_color methods.
703 // Put a single character 'A' into the buffer and check it
704 term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705 term.set_text_bg_color(Color::TransparentBg);
706 term.set_text_fg_color(Color::XtermWhite);
707 term.append("A");
708 let r = &term.u8c_disp_row(0);
709 let uc = r.col(0);
710 assert_eq!(uc.text_utf8(), b"A");
711 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712 assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713 assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714 assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715 assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717 // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718 term.clear();
719 term.set_text_fg_color_xterm(XtermColor::White);
720 term.set_text_bg_color_xterm(XtermColor::Black);
721 assert_eq!(term.text_attrib(), Attrib::Normal);
722
723 assert!(term.ansi());
724 term.append("B\x1b[32mC\x1b[1mD\n");
725
726 let r = &term.u8c_disp_row(0);
727 let uc = r.col(0);
728 assert_eq!(uc.text_utf8(), b"B");
729 assert!(uc.is_char(b'B'));
730 assert!(!uc.is_char(b'A'));
731 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735 assert_eq!(
736 &uc.charflags(),
737 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738 );
739
740 let uc = r.col(1);
741 assert_eq!(uc.text_utf8(), b"C");
742 assert!(uc.is_char(b'C'));
743 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747 assert_eq!(
748 &uc.charflags(),
749 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750 );
751
752 let uc = r.col(2);
753 assert_eq!(uc.text_utf8(), b"D");
754 assert!(uc.is_char(b'D'));
755 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757 assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759 assert_eq!(
760 &uc.attr_fgcolor(Some(term)),
761 &Color::from_rgb(0x20, 0xf0, 0x20)
762 );
763 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764 assert_eq!(
765 &uc.charflags(),
766 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767 );
768
769 // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770 term.clear();
771 term.set_text_fg_color_xterm(XtermColor::White);
772 term.set_text_bg_color_xterm(XtermColor::Black);
773 term.set_text_attrib(Attrib::Normal);
774 assert_eq!(term.text_attrib(), Attrib::Normal);
775
776 assert!(term.ansi());
777 term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779 let r = &term.u8c_disp_row(0);
780 let uc = r.col(0);
781 assert_eq!(uc.text_utf8(), b"B");
782 assert!(uc.is_char(b'B'));
783 assert!(!uc.is_char(b'A'));
784 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788 assert_eq!(
789 &uc.charflags(),
790 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791 );
792
793 let uc = r.col(1);
794 assert_eq!(uc.text_utf8(), b"C");
795 assert!(uc.is_char(b'C'));
796 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800 assert_eq!(
801 &uc.charflags(),
802 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803 );
804
805 let uc = r.col(2);
806 assert_eq!(uc.text_utf8(), b"D");
807 assert!(uc.is_char(b'D'));
808 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812 assert_eq!(
813 &uc.charflags(),
814 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815 );
816
817 let uc = r.col(3);
818 assert_eq!(uc.text_utf8(), b"E");
819 assert!(uc.is_char(b'E'));
820 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822 assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823 assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824 assert_eq!(
825 &uc.charflags(),
826 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827 );
828
829 // Test some miscellaneous Utf8 constants
830 assert_eq!(uc.length(), 1);
831 assert_eq!(uc.max_utf8(), 4);
832 assert_eq!(uc.pwidth(), 9.0);
833 assert_eq!(uc.pwidth_int(), 9);
834
835 term.set_text_fg_color_xterm(XtermColor::White);
836 term.set_text_bg_color_xterm(XtermColor::Black);
837 term.clear();
838 term.set_text_attrib(Attrib::Normal);
839
840 // Mouse selection functions
841 term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842 term.clear_mouse_selection();
843 assert_eq!(term.get_selection(), None);
844
845 // Play with cursor position
846 term.append("0123456789\n"); // Set up test pattern
847 term.append("ABCDEFGHIJ\n");
848 term.append("abcdefghij\n");
849
850 term.set_cursor_row(1);
851 assert_eq!(term.cursor_row(), 1);
852 term.set_cursor_col(1);
853 assert_eq!(term.cursor_col(), 1);
854 assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856 term.append("----"); // Overwrites text at cursor and moves cursor forward
857 assert_eq!(term.cursor_row(), 1);
858 assert_eq!(term.cursor_col(), 5);
859 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860 term.set_cursor_col(1);
861 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863 term.cursor_up(1, false);
864 assert_eq!(term.cursor_row(), 0);
865 assert_eq!(term.cursor_col(), 1);
866 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868 // Hit top of screen, so nothing happens
869 term.cursor_up(1, false);
870 assert_eq!(term.cursor_row(), 0);
871 assert_eq!(term.cursor_col(), 1);
872 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874 // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875 term.cursor_up(1, true);
876 assert_eq!(term.cursor_row(), 0);
877 assert_eq!(term.cursor_col(), 1);
878 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880 // Go back down to the overwritten text
881 term.cursor_down(2, false);
882 assert_eq!(term.cursor_row(), 2);
883 assert_eq!(term.cursor_col(), 1);
884 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886 // Go right past the overwritten text
887 term.cursor_right(4, false);
888 assert_eq!(term.cursor_row(), 2);
889 assert_eq!(term.cursor_col(), 5);
890 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892 // Go left to the end of the overwritten text
893 term.cursor_left(1);
894 assert_eq!(term.cursor_row(), 2);
895 assert_eq!(term.cursor_col(), 4);
896 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898 // Scroll back down, removing the blank line at the top.
899 // Cursor stays in place, the text moves under it.
900 term.scroll(1);
901 assert_eq!(term.cursor_row(), 2);
902 assert_eq!(term.cursor_col(), 4);
903 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905 // Clear from here to end-of-line
906 term.clear_eol();
907 assert_eq!(term.cursor_row(), 2);
908 assert_eq!(term.cursor_col(), 4);
909 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911 // Now clear from here to start-of-line. Cursor does not move.
912 term.clear_sol();
913 assert_eq!(term.cursor_row(), 2);
914 assert_eq!(term.cursor_col(), 4);
915 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916 term.cursor_left(1);
917 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918 term.set_cursor_col(0);
919 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921 // Clear some lines
922 term.clear_line(1);
923 assert_eq!(term.cursor_row(), 2);
924 assert_eq!(term.cursor_col(), 0);
925 term.set_cursor_row(1);
926 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927 term.set_cursor_row(3);
928 term.clear_cur_line();
929 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930 assert_eq!(term.cursor_row(), 3);
931 assert_eq!(term.cursor_col(), 0);
932
933 term.append("Two lines above are intentionally left blank.\n");
934 assert_eq!(term.cursor_row(), 4);
935 assert_eq!(term.cursor_col(), 0);
936
937 // Set up the test pattern again, then play with insert/delete
938 term.append("0123456789\n");
939 term.append("ABCDEFGHIJ\n");
940 term.append("abcdefghij\n");
941 assert_eq!(term.cursor_row(), 7);
942
943 term.set_cursor_row(4);
944 term.set_cursor_col(4);
945 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947 term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949 term.cursor_right(5, false);
950 assert_eq!(term.cursor_col(), 9);
951 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953 // Insert two blank rows above cursor. Cursor stays put.
954 term.insert_rows(2);
955 assert_eq!(term.cursor_row(), 4);
956 assert_eq!(term.cursor_col(), 9);
957 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958 term.cursor_down(2, false); // Go down to find our text again
959 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961 // Go back to the beginning of the inserted 'x' characters and delete them.
962 term.cursor_left(5);
963 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964 term.delete_cur_chars(5);
965 assert_eq!(term.cursor_row(), 6);
966 assert_eq!(term.cursor_col(), 4);
967 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969 term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970 term.cursor_down(1, false);
971 term.cursor_left(2);
972 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974 term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975 assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976 term.cursor_up(1, false);
977 term.delete_rows(2); // Delete remains of test pattern
978
979 term.set_text_attrib(Attrib::Bold);
980 term.insert_char_eol('-', 3, 15, 20);
981 term.set_cursor_row(3);
982 term.set_cursor_col(15);
983 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984 assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986 term.set_text_attrib(Attrib::Italic);
987 term.append(" and all lines below");
988 term.set_text_attrib(Attrib::Normal);
989 term.cursor_down(1, false);
990
991 let mut hsb = term.hscrollbar();
992 let mut sb = term.scrollbar();
993 hsb.set_value(100.0);
994 assert_eq!(hsb.value(), 100.0);
995 sb.set_value(50.0);
996 assert_eq!(sb.value(), 50.0);
997 hsb.set_value(0.0);
998 assert_eq!(hsb.value(), 0.0);
999 sb.set_value(0.0);
1000 assert_eq!(sb.value(), 0.0);
1001}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
425//--------------------------------------------------------------------------------------
426/// More tests that run when the menu bar Test1 is clicked
427fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
428 term.take_focus().unwrap();
429 term.reset_terminal();
430 term.append("0123456789 0\n");
431 term.append("0123456789 1\n");
432 term.append("0123456789 2\n");
433 term.append("0123456789 3\n");
434 term.append("0123456789 4\n");
435 term.append("0123456789 5\n");
436 term.append("0123456789 6\n");
437 term.append("0123456789 7\n");
438 term.append("0123456789 8\n");
439 term.append("0123456789 9\n");
440 term.append("------------\n");
441
442 term.set_text_fg_color(Color::Green);
443 term.plot_char('A', 0, 0);
444 term.plot_char('B', 1, 1);
445 term.plot_char('C', 2, 2);
446 term.plot_char('D', 3, 3);
447 term.plot_char('E', 4, 4);
448 term.plot_char('F', 5, 5);
449 term.set_text_fg_color(Color::XtermWhite);
450
451 assert_eq!(term.cursor_row(), 11);
452 assert_eq!(term.cursor_col(), 0);
453
454 term.set_text_bg_color(Color::DarkBlue);
455 term.plot_char_utf8('b', 8, 1);
456 term.plot_char_utf8('↑', 9, 1);
457 term.plot_char_utf8('c', 8, 2);
458 term.plot_char_utf8('↑', 9, 2);
459 term.plot_char_utf8('d', 8, 3);
460 term.plot_char_utf8('↑', 9, 3);
461 term.plot_char_utf8('e', 8, 4);
462 term.plot_char_utf8('↑', 9, 4);
463 term.plot_char_utf8('f', 8, 5);
464 term.plot_char_utf8('↑', 9, 5);
465 term.plot_char_utf8('g', 8, 6);
466 term.plot_char_utf8('↑', 9, 6);
467 term.set_text_bg_color(Color::TransparentBg);
468
469 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
470 term.append("Done!\n");
471 term.set_text_attrib(Attrib::Normal);
472}
473
474//--------------------------------------------------------------------------------------
475/// More tests that run when the menu bar button Test2 is clicked
476fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
477 term.take_focus().unwrap();
478 term.reset_terminal();
479
480 for i in 0..50 {
481 term.append(&format!("{i}\n"));
482 }
483 assert_eq!(term.history_rows(), 100);
484
485 term.clear_history();
486 assert_eq!(term.history_use(), 0);
487
488 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
489 term.append("\nDone!\n");
490 term.set_text_attrib(Attrib::Normal);
491}
492
493//--------------------------------------------------------------------------------------
494/// Another set of tests that run when Test3 is clicked
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496 term.take_focus().unwrap();
497 term.reset_terminal();
498 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500 assert_eq!(term.history_use(), 0);
501 term.clear();
502 assert_eq!(term.cursor_row(), 0);
503 assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505 term.append("Test\ntext\na\nb\nc\nd");
506 assert_eq!(term.cursor_row(), 5);
507 let hist = term.history_use();
508 term.clear_screen_home(false);
509 assert_eq!(term.cursor_row(), 0);
510 assert_eq!(term.history_use(), hist); // History not changed
511
512 term.append("Test\ntext\na\nb\nc\nd\ne");
513 assert_eq!(term.cursor_row(), 6);
514 term.clear_screen_home(true);
515 assert_eq!(term.cursor_row(), 0);
516
517 term.append("Test\ntext\na\nb\nc\n");
518 assert_eq!(term.cursor_row(), 5);
519 term.clear_to_color(Color::DarkBlue);
520 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521 assert_eq!(term.text_bg_color(), Color::TransparentBg);
522 assert_eq!(term.cursor_row(), 0);
523
524 // Test cursor_home()
525 term.append("Test\n\n\n\n\n\n\n\n\n\n");
526 assert_eq!(term.cursor_row(), 10);
527 term.cursor_home();
528 assert_eq!(term.cursor_row(), 0);
529
530 // Test the widget color
531 assert_eq!(term.color(), Color::Black); // Default
532 term.set_color(Color::DarkGreen);
533 assert_eq!(term.color(), Color::DarkGreen);
534 term.set_color(Color::Black);
535 assert_eq!(term.color(), Color::Black);
536 term.append(
537 "This should be one line of white text on black, embedded into the top of a blue field.\n",
538 );
539
540 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541 term.set_output_translate(OutFlags::OFF);
542 assert_eq!(term.output_translate(), OutFlags::OFF);
543 term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547 term.append("\nDone!\n");
548 term.set_text_attrib(Attrib::Normal);
549}
550
551//--------------------------------------------------------------------------------------
552/// Another set of tests for the ring-buffer access methods
553/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555 let sel_len = term.selection_text_len();
556 let sel = term.selection_text();
557
558 term.take_focus().unwrap();
559 term.reset_terminal();
560 // Test the Utf8Char primitive
561 let uc = Utf8Char::new(b'Q');
562 let uc1 = uc.text_utf8();
563 assert_eq!(&uc1, b"Q");
564 assert_eq!(&uc.attrib(), &Attrib::Normal);
565 assert_eq!(
566 &uc.charflags(),
567 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568 );
569 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570 assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572 let ring_rows = term.ring_rows();
573
574 term.take_focus().unwrap();
575 term.clear_history();
576 assert_eq!(term.history_use(), 0);
577
578 // Subtract row numbers, modulo `rows`
579 fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580 match a - b {
581 n if n < 0 => n + rows,
582 n => n,
583 }
584 }
585 // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587 assert!(term.disp_srow() >= 0);
588 assert!(term.disp_erow() >= 0);
589 assert!(term.hist_srow() >= 0);
590 assert!(term.hist_erow() >= 0);
591 assert!(term.offset() >= 0);
592 assert!(term.disp_srow() <= ring_rows);
593 assert!(term.disp_erow() <= ring_rows);
594 assert!(term.hist_srow() <= ring_rows);
595 assert!(term.hist_erow() <= ring_rows);
596 assert!(term.offset() <= ring_rows);
597
598 assert_eq!(term.ring_srow(), 0);
599 assert_eq!(term.ring_erow(), ring_rows - 1);
600 assert_eq!(
601 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602 term.display_rows()
603 );
604 assert_eq!(
605 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606 term.history_rows()
607 );
608
609 assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610 assert_eq!(term.ring_srow(), 0);
611
612 /// Local function to read back all rows from the display into a long string.
613 /// Does not include scrollback history.
614 /// Trims trailing blanks on each line
615 fn read_disp(term: &Terminal) -> String {
616 let rows = term.display_rows();
617 let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618 for row in 0..rows {
619 let r = term.u8c_disp_row(row).trim();
620 // Iterate through a row, accumulating [u8]
621 for c in r.iter() {
622 // Note: Sometimes utf-8 length is > 1
623 text.extend_from_slice(c.text_utf8());
624 }
625 text.extend_from_slice(b"\n");
626 }
627 // Return the result as a string
628 std::str::from_utf8(&text).unwrap().to_string()
629 }
630
631 term.clear();
632 term.append("Top line ↑ (up-arrow)");
633 term.set_text_attrib(Attrib::Underline);
634 term.append(" ");
635 term.set_text_attrib(Attrib::Normal);
636 term.append(" \n");
637 let mut text_out = read_disp(term);
638 // Trim trailing empty lines
639 text_out = text_out.trim_end_matches(&"\n").to_string();
640 // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642 assert_eq!(text_out, "Top line ↑ (up-arrow) ");
643 let r = term.u8c_disp_row(0);
644 assert_eq!(r.col(0).text_utf8(), b"T");
645 assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646 assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647 let r = term.u8c_disp_row(1);
648 assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649 assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651 // Clear the screen again, then append test text, then read it back and compare
652 let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656 Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659 term.clear_history();
660 term.clear();
661 let bg_save = term.text_bg_color();
662 let fg_save = term.text_fg_color();
663 term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664 term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665 term.append(test_text);
666 term.set_text_bg_color(bg_save);
667 term.set_text_fg_color(fg_save);
668
669 let mut text_out = read_disp(term);
670 // Trim trailing empty lines
671 text_out = text_out.trim_end_matches(&"\n").to_string();
672 assert_eq!(test_text, text_out);
673
674 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676 assert_eq!(term.ring_srow(), 0);
677 assert_eq!(term.ring_erow(), ring_rows - 1);
678 assert_eq!(
679 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680 term.display_rows()
681 );
682 assert_eq!(
683 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684 term.history_rows()
685 );
686
687 term.append(&format!(
688 "\n\nScreen has {} rows of {} columns.\n",
689 term.display_rows(),
690 term.display_columns()
691 ));
692
693 term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
695
696//--------------------------------------------------------------------------------------
697/// Yet another set of tests for misc cursor functions and other stuff
698/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700 term.take_focus().unwrap();
701
702 // Test the attr_fg_color and attr_bg_color methods.
703 // Put a single character 'A' into the buffer and check it
704 term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705 term.set_text_bg_color(Color::TransparentBg);
706 term.set_text_fg_color(Color::XtermWhite);
707 term.append("A");
708 let r = &term.u8c_disp_row(0);
709 let uc = r.col(0);
710 assert_eq!(uc.text_utf8(), b"A");
711 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712 assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713 assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714 assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715 assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717 // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718 term.clear();
719 term.set_text_fg_color_xterm(XtermColor::White);
720 term.set_text_bg_color_xterm(XtermColor::Black);
721 assert_eq!(term.text_attrib(), Attrib::Normal);
722
723 assert!(term.ansi());
724 term.append("B\x1b[32mC\x1b[1mD\n");
725
726 let r = &term.u8c_disp_row(0);
727 let uc = r.col(0);
728 assert_eq!(uc.text_utf8(), b"B");
729 assert!(uc.is_char(b'B'));
730 assert!(!uc.is_char(b'A'));
731 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735 assert_eq!(
736 &uc.charflags(),
737 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738 );
739
740 let uc = r.col(1);
741 assert_eq!(uc.text_utf8(), b"C");
742 assert!(uc.is_char(b'C'));
743 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747 assert_eq!(
748 &uc.charflags(),
749 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750 );
751
752 let uc = r.col(2);
753 assert_eq!(uc.text_utf8(), b"D");
754 assert!(uc.is_char(b'D'));
755 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757 assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759 assert_eq!(
760 &uc.attr_fgcolor(Some(term)),
761 &Color::from_rgb(0x20, 0xf0, 0x20)
762 );
763 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764 assert_eq!(
765 &uc.charflags(),
766 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767 );
768
769 // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770 term.clear();
771 term.set_text_fg_color_xterm(XtermColor::White);
772 term.set_text_bg_color_xterm(XtermColor::Black);
773 term.set_text_attrib(Attrib::Normal);
774 assert_eq!(term.text_attrib(), Attrib::Normal);
775
776 assert!(term.ansi());
777 term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779 let r = &term.u8c_disp_row(0);
780 let uc = r.col(0);
781 assert_eq!(uc.text_utf8(), b"B");
782 assert!(uc.is_char(b'B'));
783 assert!(!uc.is_char(b'A'));
784 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788 assert_eq!(
789 &uc.charflags(),
790 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791 );
792
793 let uc = r.col(1);
794 assert_eq!(uc.text_utf8(), b"C");
795 assert!(uc.is_char(b'C'));
796 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800 assert_eq!(
801 &uc.charflags(),
802 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803 );
804
805 let uc = r.col(2);
806 assert_eq!(uc.text_utf8(), b"D");
807 assert!(uc.is_char(b'D'));
808 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812 assert_eq!(
813 &uc.charflags(),
814 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815 );
816
817 let uc = r.col(3);
818 assert_eq!(uc.text_utf8(), b"E");
819 assert!(uc.is_char(b'E'));
820 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822 assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823 assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824 assert_eq!(
825 &uc.charflags(),
826 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827 );
828
829 // Test some miscellaneous Utf8 constants
830 assert_eq!(uc.length(), 1);
831 assert_eq!(uc.max_utf8(), 4);
832 assert_eq!(uc.pwidth(), 9.0);
833 assert_eq!(uc.pwidth_int(), 9);
834
835 term.set_text_fg_color_xterm(XtermColor::White);
836 term.set_text_bg_color_xterm(XtermColor::Black);
837 term.clear();
838 term.set_text_attrib(Attrib::Normal);
839
840 // Mouse selection functions
841 term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842 term.clear_mouse_selection();
843 assert_eq!(term.get_selection(), None);
844
845 // Play with cursor position
846 term.append("0123456789\n"); // Set up test pattern
847 term.append("ABCDEFGHIJ\n");
848 term.append("abcdefghij\n");
849
850 term.set_cursor_row(1);
851 assert_eq!(term.cursor_row(), 1);
852 term.set_cursor_col(1);
853 assert_eq!(term.cursor_col(), 1);
854 assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856 term.append("----"); // Overwrites text at cursor and moves cursor forward
857 assert_eq!(term.cursor_row(), 1);
858 assert_eq!(term.cursor_col(), 5);
859 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860 term.set_cursor_col(1);
861 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863 term.cursor_up(1, false);
864 assert_eq!(term.cursor_row(), 0);
865 assert_eq!(term.cursor_col(), 1);
866 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868 // Hit top of screen, so nothing happens
869 term.cursor_up(1, false);
870 assert_eq!(term.cursor_row(), 0);
871 assert_eq!(term.cursor_col(), 1);
872 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874 // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875 term.cursor_up(1, true);
876 assert_eq!(term.cursor_row(), 0);
877 assert_eq!(term.cursor_col(), 1);
878 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880 // Go back down to the overwritten text
881 term.cursor_down(2, false);
882 assert_eq!(term.cursor_row(), 2);
883 assert_eq!(term.cursor_col(), 1);
884 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886 // Go right past the overwritten text
887 term.cursor_right(4, false);
888 assert_eq!(term.cursor_row(), 2);
889 assert_eq!(term.cursor_col(), 5);
890 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892 // Go left to the end of the overwritten text
893 term.cursor_left(1);
894 assert_eq!(term.cursor_row(), 2);
895 assert_eq!(term.cursor_col(), 4);
896 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898 // Scroll back down, removing the blank line at the top.
899 // Cursor stays in place, the text moves under it.
900 term.scroll(1);
901 assert_eq!(term.cursor_row(), 2);
902 assert_eq!(term.cursor_col(), 4);
903 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905 // Clear from here to end-of-line
906 term.clear_eol();
907 assert_eq!(term.cursor_row(), 2);
908 assert_eq!(term.cursor_col(), 4);
909 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911 // Now clear from here to start-of-line. Cursor does not move.
912 term.clear_sol();
913 assert_eq!(term.cursor_row(), 2);
914 assert_eq!(term.cursor_col(), 4);
915 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916 term.cursor_left(1);
917 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918 term.set_cursor_col(0);
919 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921 // Clear some lines
922 term.clear_line(1);
923 assert_eq!(term.cursor_row(), 2);
924 assert_eq!(term.cursor_col(), 0);
925 term.set_cursor_row(1);
926 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927 term.set_cursor_row(3);
928 term.clear_cur_line();
929 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930 assert_eq!(term.cursor_row(), 3);
931 assert_eq!(term.cursor_col(), 0);
932
933 term.append("Two lines above are intentionally left blank.\n");
934 assert_eq!(term.cursor_row(), 4);
935 assert_eq!(term.cursor_col(), 0);
936
937 // Set up the test pattern again, then play with insert/delete
938 term.append("0123456789\n");
939 term.append("ABCDEFGHIJ\n");
940 term.append("abcdefghij\n");
941 assert_eq!(term.cursor_row(), 7);
942
943 term.set_cursor_row(4);
944 term.set_cursor_col(4);
945 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947 term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949 term.cursor_right(5, false);
950 assert_eq!(term.cursor_col(), 9);
951 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953 // Insert two blank rows above cursor. Cursor stays put.
954 term.insert_rows(2);
955 assert_eq!(term.cursor_row(), 4);
956 assert_eq!(term.cursor_col(), 9);
957 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958 term.cursor_down(2, false); // Go down to find our text again
959 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961 // Go back to the beginning of the inserted 'x' characters and delete them.
962 term.cursor_left(5);
963 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964 term.delete_cur_chars(5);
965 assert_eq!(term.cursor_row(), 6);
966 assert_eq!(term.cursor_col(), 4);
967 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969 term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970 term.cursor_down(1, false);
971 term.cursor_left(2);
972 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974 term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975 assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976 term.cursor_up(1, false);
977 term.delete_rows(2); // Delete remains of test pattern
978
979 term.set_text_attrib(Attrib::Bold);
980 term.insert_char_eol('-', 3, 15, 20);
981 term.set_cursor_row(3);
982 term.set_cursor_col(15);
983 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984 assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986 term.set_text_attrib(Attrib::Italic);
987 term.append(" and all lines below");
988 term.set_text_attrib(Attrib::Normal);
989 term.cursor_down(1, false);
990
991 let mut hsb = term.hscrollbar();
992 let mut sb = term.scrollbar();
993 hsb.set_value(100.0);
994 assert_eq!(hsb.value(), 100.0);
995 sb.set_value(50.0);
996 assert_eq!(sb.value(), 50.0);
997 hsb.set_value(0.0);
998 assert_eq!(hsb.value(), 0.0);
999 sb.set_value(0.0);
1000 assert_eq!(sb.value(), 0.0);
1001}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
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?
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496 term.take_focus().unwrap();
497 term.reset_terminal();
498 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500 assert_eq!(term.history_use(), 0);
501 term.clear();
502 assert_eq!(term.cursor_row(), 0);
503 assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505 term.append("Test\ntext\na\nb\nc\nd");
506 assert_eq!(term.cursor_row(), 5);
507 let hist = term.history_use();
508 term.clear_screen_home(false);
509 assert_eq!(term.cursor_row(), 0);
510 assert_eq!(term.history_use(), hist); // History not changed
511
512 term.append("Test\ntext\na\nb\nc\nd\ne");
513 assert_eq!(term.cursor_row(), 6);
514 term.clear_screen_home(true);
515 assert_eq!(term.cursor_row(), 0);
516
517 term.append("Test\ntext\na\nb\nc\n");
518 assert_eq!(term.cursor_row(), 5);
519 term.clear_to_color(Color::DarkBlue);
520 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521 assert_eq!(term.text_bg_color(), Color::TransparentBg);
522 assert_eq!(term.cursor_row(), 0);
523
524 // Test cursor_home()
525 term.append("Test\n\n\n\n\n\n\n\n\n\n");
526 assert_eq!(term.cursor_row(), 10);
527 term.cursor_home();
528 assert_eq!(term.cursor_row(), 0);
529
530 // Test the widget color
531 assert_eq!(term.color(), Color::Black); // Default
532 term.set_color(Color::DarkGreen);
533 assert_eq!(term.color(), Color::DarkGreen);
534 term.set_color(Color::Black);
535 assert_eq!(term.color(), Color::Black);
536 term.append(
537 "This should be one line of white text on black, embedded into the top of a blue field.\n",
538 );
539
540 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541 term.set_output_translate(OutFlags::OFF);
542 assert_eq!(term.output_translate(), OutFlags::OFF);
543 term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547 term.append("\nDone!\n");
548 term.set_text_attrib(Attrib::Normal);
549}
550
551//--------------------------------------------------------------------------------------
552/// Another set of tests for the ring-buffer access methods
553/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555 let sel_len = term.selection_text_len();
556 let sel = term.selection_text();
557
558 term.take_focus().unwrap();
559 term.reset_terminal();
560 // Test the Utf8Char primitive
561 let uc = Utf8Char::new(b'Q');
562 let uc1 = uc.text_utf8();
563 assert_eq!(&uc1, b"Q");
564 assert_eq!(&uc.attrib(), &Attrib::Normal);
565 assert_eq!(
566 &uc.charflags(),
567 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568 );
569 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570 assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572 let ring_rows = term.ring_rows();
573
574 term.take_focus().unwrap();
575 term.clear_history();
576 assert_eq!(term.history_use(), 0);
577
578 // Subtract row numbers, modulo `rows`
579 fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580 match a - b {
581 n if n < 0 => n + rows,
582 n => n,
583 }
584 }
585 // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587 assert!(term.disp_srow() >= 0);
588 assert!(term.disp_erow() >= 0);
589 assert!(term.hist_srow() >= 0);
590 assert!(term.hist_erow() >= 0);
591 assert!(term.offset() >= 0);
592 assert!(term.disp_srow() <= ring_rows);
593 assert!(term.disp_erow() <= ring_rows);
594 assert!(term.hist_srow() <= ring_rows);
595 assert!(term.hist_erow() <= ring_rows);
596 assert!(term.offset() <= ring_rows);
597
598 assert_eq!(term.ring_srow(), 0);
599 assert_eq!(term.ring_erow(), ring_rows - 1);
600 assert_eq!(
601 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602 term.display_rows()
603 );
604 assert_eq!(
605 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606 term.history_rows()
607 );
608
609 assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610 assert_eq!(term.ring_srow(), 0);
611
612 /// Local function to read back all rows from the display into a long string.
613 /// Does not include scrollback history.
614 /// Trims trailing blanks on each line
615 fn read_disp(term: &Terminal) -> String {
616 let rows = term.display_rows();
617 let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618 for row in 0..rows {
619 let r = term.u8c_disp_row(row).trim();
620 // Iterate through a row, accumulating [u8]
621 for c in r.iter() {
622 // Note: Sometimes utf-8 length is > 1
623 text.extend_from_slice(c.text_utf8());
624 }
625 text.extend_from_slice(b"\n");
626 }
627 // Return the result as a string
628 std::str::from_utf8(&text).unwrap().to_string()
629 }
630
631 term.clear();
632 term.append("Top line ↑ (up-arrow)");
633 term.set_text_attrib(Attrib::Underline);
634 term.append(" ");
635 term.set_text_attrib(Attrib::Normal);
636 term.append(" \n");
637 let mut text_out = read_disp(term);
638 // Trim trailing empty lines
639 text_out = text_out.trim_end_matches(&"\n").to_string();
640 // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642 assert_eq!(text_out, "Top line ↑ (up-arrow) ");
643 let r = term.u8c_disp_row(0);
644 assert_eq!(r.col(0).text_utf8(), b"T");
645 assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646 assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647 let r = term.u8c_disp_row(1);
648 assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649 assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651 // Clear the screen again, then append test text, then read it back and compare
652 let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656 Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659 term.clear_history();
660 term.clear();
661 let bg_save = term.text_bg_color();
662 let fg_save = term.text_fg_color();
663 term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664 term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665 term.append(test_text);
666 term.set_text_bg_color(bg_save);
667 term.set_text_fg_color(fg_save);
668
669 let mut text_out = read_disp(term);
670 // Trim trailing empty lines
671 text_out = text_out.trim_end_matches(&"\n").to_string();
672 assert_eq!(test_text, text_out);
673
674 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676 assert_eq!(term.ring_srow(), 0);
677 assert_eq!(term.ring_erow(), ring_rows - 1);
678 assert_eq!(
679 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680 term.display_rows()
681 );
682 assert_eq!(
683 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684 term.history_rows()
685 );
686
687 term.append(&format!(
688 "\n\nScreen has {} rows of {} columns.\n",
689 term.display_rows(),
690 term.display_columns()
691 ));
692
693 term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
695
696//--------------------------------------------------------------------------------------
697/// Yet another set of tests for misc cursor functions and other stuff
698/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700 term.take_focus().unwrap();
701
702 // Test the attr_fg_color and attr_bg_color methods.
703 // Put a single character 'A' into the buffer and check it
704 term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705 term.set_text_bg_color(Color::TransparentBg);
706 term.set_text_fg_color(Color::XtermWhite);
707 term.append("A");
708 let r = &term.u8c_disp_row(0);
709 let uc = r.col(0);
710 assert_eq!(uc.text_utf8(), b"A");
711 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712 assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713 assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714 assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715 assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717 // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718 term.clear();
719 term.set_text_fg_color_xterm(XtermColor::White);
720 term.set_text_bg_color_xterm(XtermColor::Black);
721 assert_eq!(term.text_attrib(), Attrib::Normal);
722
723 assert!(term.ansi());
724 term.append("B\x1b[32mC\x1b[1mD\n");
725
726 let r = &term.u8c_disp_row(0);
727 let uc = r.col(0);
728 assert_eq!(uc.text_utf8(), b"B");
729 assert!(uc.is_char(b'B'));
730 assert!(!uc.is_char(b'A'));
731 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735 assert_eq!(
736 &uc.charflags(),
737 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738 );
739
740 let uc = r.col(1);
741 assert_eq!(uc.text_utf8(), b"C");
742 assert!(uc.is_char(b'C'));
743 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747 assert_eq!(
748 &uc.charflags(),
749 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750 );
751
752 let uc = r.col(2);
753 assert_eq!(uc.text_utf8(), b"D");
754 assert!(uc.is_char(b'D'));
755 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757 assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759 assert_eq!(
760 &uc.attr_fgcolor(Some(term)),
761 &Color::from_rgb(0x20, 0xf0, 0x20)
762 );
763 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764 assert_eq!(
765 &uc.charflags(),
766 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767 );
768
769 // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770 term.clear();
771 term.set_text_fg_color_xterm(XtermColor::White);
772 term.set_text_bg_color_xterm(XtermColor::Black);
773 term.set_text_attrib(Attrib::Normal);
774 assert_eq!(term.text_attrib(), Attrib::Normal);
775
776 assert!(term.ansi());
777 term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779 let r = &term.u8c_disp_row(0);
780 let uc = r.col(0);
781 assert_eq!(uc.text_utf8(), b"B");
782 assert!(uc.is_char(b'B'));
783 assert!(!uc.is_char(b'A'));
784 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788 assert_eq!(
789 &uc.charflags(),
790 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791 );
792
793 let uc = r.col(1);
794 assert_eq!(uc.text_utf8(), b"C");
795 assert!(uc.is_char(b'C'));
796 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800 assert_eq!(
801 &uc.charflags(),
802 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803 );
804
805 let uc = r.col(2);
806 assert_eq!(uc.text_utf8(), b"D");
807 assert!(uc.is_char(b'D'));
808 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812 assert_eq!(
813 &uc.charflags(),
814 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815 );
816
817 let uc = r.col(3);
818 assert_eq!(uc.text_utf8(), b"E");
819 assert!(uc.is_char(b'E'));
820 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822 assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823 assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824 assert_eq!(
825 &uc.charflags(),
826 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827 );
828
829 // Test some miscellaneous Utf8 constants
830 assert_eq!(uc.length(), 1);
831 assert_eq!(uc.max_utf8(), 4);
832 assert_eq!(uc.pwidth(), 9.0);
833 assert_eq!(uc.pwidth_int(), 9);
834
835 term.set_text_fg_color_xterm(XtermColor::White);
836 term.set_text_bg_color_xterm(XtermColor::Black);
837 term.clear();
838 term.set_text_attrib(Attrib::Normal);
839
840 // Mouse selection functions
841 term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842 term.clear_mouse_selection();
843 assert_eq!(term.get_selection(), None);
844
845 // Play with cursor position
846 term.append("0123456789\n"); // Set up test pattern
847 term.append("ABCDEFGHIJ\n");
848 term.append("abcdefghij\n");
849
850 term.set_cursor_row(1);
851 assert_eq!(term.cursor_row(), 1);
852 term.set_cursor_col(1);
853 assert_eq!(term.cursor_col(), 1);
854 assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856 term.append("----"); // Overwrites text at cursor and moves cursor forward
857 assert_eq!(term.cursor_row(), 1);
858 assert_eq!(term.cursor_col(), 5);
859 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860 term.set_cursor_col(1);
861 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863 term.cursor_up(1, false);
864 assert_eq!(term.cursor_row(), 0);
865 assert_eq!(term.cursor_col(), 1);
866 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868 // Hit top of screen, so nothing happens
869 term.cursor_up(1, false);
870 assert_eq!(term.cursor_row(), 0);
871 assert_eq!(term.cursor_col(), 1);
872 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874 // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875 term.cursor_up(1, true);
876 assert_eq!(term.cursor_row(), 0);
877 assert_eq!(term.cursor_col(), 1);
878 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880 // Go back down to the overwritten text
881 term.cursor_down(2, false);
882 assert_eq!(term.cursor_row(), 2);
883 assert_eq!(term.cursor_col(), 1);
884 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886 // Go right past the overwritten text
887 term.cursor_right(4, false);
888 assert_eq!(term.cursor_row(), 2);
889 assert_eq!(term.cursor_col(), 5);
890 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892 // Go left to the end of the overwritten text
893 term.cursor_left(1);
894 assert_eq!(term.cursor_row(), 2);
895 assert_eq!(term.cursor_col(), 4);
896 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898 // Scroll back down, removing the blank line at the top.
899 // Cursor stays in place, the text moves under it.
900 term.scroll(1);
901 assert_eq!(term.cursor_row(), 2);
902 assert_eq!(term.cursor_col(), 4);
903 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905 // Clear from here to end-of-line
906 term.clear_eol();
907 assert_eq!(term.cursor_row(), 2);
908 assert_eq!(term.cursor_col(), 4);
909 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911 // Now clear from here to start-of-line. Cursor does not move.
912 term.clear_sol();
913 assert_eq!(term.cursor_row(), 2);
914 assert_eq!(term.cursor_col(), 4);
915 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916 term.cursor_left(1);
917 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918 term.set_cursor_col(0);
919 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921 // Clear some lines
922 term.clear_line(1);
923 assert_eq!(term.cursor_row(), 2);
924 assert_eq!(term.cursor_col(), 0);
925 term.set_cursor_row(1);
926 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927 term.set_cursor_row(3);
928 term.clear_cur_line();
929 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930 assert_eq!(term.cursor_row(), 3);
931 assert_eq!(term.cursor_col(), 0);
932
933 term.append("Two lines above are intentionally left blank.\n");
934 assert_eq!(term.cursor_row(), 4);
935 assert_eq!(term.cursor_col(), 0);
936
937 // Set up the test pattern again, then play with insert/delete
938 term.append("0123456789\n");
939 term.append("ABCDEFGHIJ\n");
940 term.append("abcdefghij\n");
941 assert_eq!(term.cursor_row(), 7);
942
943 term.set_cursor_row(4);
944 term.set_cursor_col(4);
945 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947 term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949 term.cursor_right(5, false);
950 assert_eq!(term.cursor_col(), 9);
951 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953 // Insert two blank rows above cursor. Cursor stays put.
954 term.insert_rows(2);
955 assert_eq!(term.cursor_row(), 4);
956 assert_eq!(term.cursor_col(), 9);
957 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958 term.cursor_down(2, false); // Go down to find our text again
959 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961 // Go back to the beginning of the inserted 'x' characters and delete them.
962 term.cursor_left(5);
963 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964 term.delete_cur_chars(5);
965 assert_eq!(term.cursor_row(), 6);
966 assert_eq!(term.cursor_col(), 4);
967 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969 term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970 term.cursor_down(1, false);
971 term.cursor_left(2);
972 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974 term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975 assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976 term.cursor_up(1, false);
977 term.delete_rows(2); // Delete remains of test pattern
978
979 term.set_text_attrib(Attrib::Bold);
980 term.insert_char_eol('-', 3, 15, 20);
981 term.set_cursor_row(3);
982 term.set_cursor_col(15);
983 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984 assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986 term.set_text_attrib(Attrib::Italic);
987 term.append(" and all lines below");
988 term.set_text_attrib(Attrib::Normal);
989 term.cursor_down(1, false);
990
991 let mut hsb = term.hscrollbar();
992 let mut sb = term.scrollbar();
993 hsb.set_value(100.0);
994 assert_eq!(hsb.value(), 100.0);
995 sb.set_value(50.0);
996 assert_eq!(sb.value(), 50.0);
997 hsb.set_value(0.0);
998 assert_eq!(hsb.value(), 0.0);
999 sb.set_value(0.0);
1000 assert_eq!(sb.value(), 0.0);
1001}
Sourcepub fn clear_mouse_selection(&mut self)
pub fn clear_mouse_selection(&mut self)
Clear any current mouse selection.
Examples found in repository?
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700 term.take_focus().unwrap();
701
702 // Test the attr_fg_color and attr_bg_color methods.
703 // Put a single character 'A' into the buffer and check it
704 term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705 term.set_text_bg_color(Color::TransparentBg);
706 term.set_text_fg_color(Color::XtermWhite);
707 term.append("A");
708 let r = &term.u8c_disp_row(0);
709 let uc = r.col(0);
710 assert_eq!(uc.text_utf8(), b"A");
711 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712 assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713 assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714 assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715 assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717 // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718 term.clear();
719 term.set_text_fg_color_xterm(XtermColor::White);
720 term.set_text_bg_color_xterm(XtermColor::Black);
721 assert_eq!(term.text_attrib(), Attrib::Normal);
722
723 assert!(term.ansi());
724 term.append("B\x1b[32mC\x1b[1mD\n");
725
726 let r = &term.u8c_disp_row(0);
727 let uc = r.col(0);
728 assert_eq!(uc.text_utf8(), b"B");
729 assert!(uc.is_char(b'B'));
730 assert!(!uc.is_char(b'A'));
731 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735 assert_eq!(
736 &uc.charflags(),
737 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738 );
739
740 let uc = r.col(1);
741 assert_eq!(uc.text_utf8(), b"C");
742 assert!(uc.is_char(b'C'));
743 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747 assert_eq!(
748 &uc.charflags(),
749 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750 );
751
752 let uc = r.col(2);
753 assert_eq!(uc.text_utf8(), b"D");
754 assert!(uc.is_char(b'D'));
755 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757 assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759 assert_eq!(
760 &uc.attr_fgcolor(Some(term)),
761 &Color::from_rgb(0x20, 0xf0, 0x20)
762 );
763 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764 assert_eq!(
765 &uc.charflags(),
766 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767 );
768
769 // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770 term.clear();
771 term.set_text_fg_color_xterm(XtermColor::White);
772 term.set_text_bg_color_xterm(XtermColor::Black);
773 term.set_text_attrib(Attrib::Normal);
774 assert_eq!(term.text_attrib(), Attrib::Normal);
775
776 assert!(term.ansi());
777 term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779 let r = &term.u8c_disp_row(0);
780 let uc = r.col(0);
781 assert_eq!(uc.text_utf8(), b"B");
782 assert!(uc.is_char(b'B'));
783 assert!(!uc.is_char(b'A'));
784 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788 assert_eq!(
789 &uc.charflags(),
790 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791 );
792
793 let uc = r.col(1);
794 assert_eq!(uc.text_utf8(), b"C");
795 assert!(uc.is_char(b'C'));
796 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800 assert_eq!(
801 &uc.charflags(),
802 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803 );
804
805 let uc = r.col(2);
806 assert_eq!(uc.text_utf8(), b"D");
807 assert!(uc.is_char(b'D'));
808 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812 assert_eq!(
813 &uc.charflags(),
814 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815 );
816
817 let uc = r.col(3);
818 assert_eq!(uc.text_utf8(), b"E");
819 assert!(uc.is_char(b'E'));
820 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822 assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823 assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824 assert_eq!(
825 &uc.charflags(),
826 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827 );
828
829 // Test some miscellaneous Utf8 constants
830 assert_eq!(uc.length(), 1);
831 assert_eq!(uc.max_utf8(), 4);
832 assert_eq!(uc.pwidth(), 9.0);
833 assert_eq!(uc.pwidth_int(), 9);
834
835 term.set_text_fg_color_xterm(XtermColor::White);
836 term.set_text_bg_color_xterm(XtermColor::Black);
837 term.clear();
838 term.set_text_attrib(Attrib::Normal);
839
840 // Mouse selection functions
841 term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842 term.clear_mouse_selection();
843 assert_eq!(term.get_selection(), None);
844
845 // Play with cursor position
846 term.append("0123456789\n"); // Set up test pattern
847 term.append("ABCDEFGHIJ\n");
848 term.append("abcdefghij\n");
849
850 term.set_cursor_row(1);
851 assert_eq!(term.cursor_row(), 1);
852 term.set_cursor_col(1);
853 assert_eq!(term.cursor_col(), 1);
854 assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856 term.append("----"); // Overwrites text at cursor and moves cursor forward
857 assert_eq!(term.cursor_row(), 1);
858 assert_eq!(term.cursor_col(), 5);
859 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860 term.set_cursor_col(1);
861 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863 term.cursor_up(1, false);
864 assert_eq!(term.cursor_row(), 0);
865 assert_eq!(term.cursor_col(), 1);
866 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868 // Hit top of screen, so nothing happens
869 term.cursor_up(1, false);
870 assert_eq!(term.cursor_row(), 0);
871 assert_eq!(term.cursor_col(), 1);
872 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874 // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875 term.cursor_up(1, true);
876 assert_eq!(term.cursor_row(), 0);
877 assert_eq!(term.cursor_col(), 1);
878 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880 // Go back down to the overwritten text
881 term.cursor_down(2, false);
882 assert_eq!(term.cursor_row(), 2);
883 assert_eq!(term.cursor_col(), 1);
884 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886 // Go right past the overwritten text
887 term.cursor_right(4, false);
888 assert_eq!(term.cursor_row(), 2);
889 assert_eq!(term.cursor_col(), 5);
890 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892 // Go left to the end of the overwritten text
893 term.cursor_left(1);
894 assert_eq!(term.cursor_row(), 2);
895 assert_eq!(term.cursor_col(), 4);
896 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898 // Scroll back down, removing the blank line at the top.
899 // Cursor stays in place, the text moves under it.
900 term.scroll(1);
901 assert_eq!(term.cursor_row(), 2);
902 assert_eq!(term.cursor_col(), 4);
903 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905 // Clear from here to end-of-line
906 term.clear_eol();
907 assert_eq!(term.cursor_row(), 2);
908 assert_eq!(term.cursor_col(), 4);
909 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911 // Now clear from here to start-of-line. Cursor does not move.
912 term.clear_sol();
913 assert_eq!(term.cursor_row(), 2);
914 assert_eq!(term.cursor_col(), 4);
915 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916 term.cursor_left(1);
917 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918 term.set_cursor_col(0);
919 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921 // Clear some lines
922 term.clear_line(1);
923 assert_eq!(term.cursor_row(), 2);
924 assert_eq!(term.cursor_col(), 0);
925 term.set_cursor_row(1);
926 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927 term.set_cursor_row(3);
928 term.clear_cur_line();
929 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930 assert_eq!(term.cursor_row(), 3);
931 assert_eq!(term.cursor_col(), 0);
932
933 term.append("Two lines above are intentionally left blank.\n");
934 assert_eq!(term.cursor_row(), 4);
935 assert_eq!(term.cursor_col(), 0);
936
937 // Set up the test pattern again, then play with insert/delete
938 term.append("0123456789\n");
939 term.append("ABCDEFGHIJ\n");
940 term.append("abcdefghij\n");
941 assert_eq!(term.cursor_row(), 7);
942
943 term.set_cursor_row(4);
944 term.set_cursor_col(4);
945 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947 term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949 term.cursor_right(5, false);
950 assert_eq!(term.cursor_col(), 9);
951 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953 // Insert two blank rows above cursor. Cursor stays put.
954 term.insert_rows(2);
955 assert_eq!(term.cursor_row(), 4);
956 assert_eq!(term.cursor_col(), 9);
957 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958 term.cursor_down(2, false); // Go down to find our text again
959 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961 // Go back to the beginning of the inserted 'x' characters and delete them.
962 term.cursor_left(5);
963 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964 term.delete_cur_chars(5);
965 assert_eq!(term.cursor_row(), 6);
966 assert_eq!(term.cursor_col(), 4);
967 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969 term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970 term.cursor_down(1, false);
971 term.cursor_left(2);
972 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974 term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975 assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976 term.cursor_up(1, false);
977 term.delete_rows(2); // Delete remains of test pattern
978
979 term.set_text_attrib(Attrib::Bold);
980 term.insert_char_eol('-', 3, 15, 20);
981 term.set_cursor_row(3);
982 term.set_cursor_col(15);
983 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984 assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986 term.set_text_attrib(Attrib::Italic);
987 term.append(" and all lines below");
988 term.set_text_attrib(Attrib::Normal);
989 term.cursor_down(1, false);
990
991 let mut hsb = term.hscrollbar();
992 let mut sb = term.scrollbar();
993 hsb.set_value(100.0);
994 assert_eq!(hsb.value(), 100.0);
995 sb.set_value(50.0);
996 assert_eq!(sb.value(), 50.0);
997 hsb.set_value(0.0);
998 assert_eq!(hsb.value(), 0.0);
999 sb.set_value(0.0);
1000 assert_eq!(sb.value(), 0.0);
1001}
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?
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496 term.take_focus().unwrap();
497 term.reset_terminal();
498 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500 assert_eq!(term.history_use(), 0);
501 term.clear();
502 assert_eq!(term.cursor_row(), 0);
503 assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505 term.append("Test\ntext\na\nb\nc\nd");
506 assert_eq!(term.cursor_row(), 5);
507 let hist = term.history_use();
508 term.clear_screen_home(false);
509 assert_eq!(term.cursor_row(), 0);
510 assert_eq!(term.history_use(), hist); // History not changed
511
512 term.append("Test\ntext\na\nb\nc\nd\ne");
513 assert_eq!(term.cursor_row(), 6);
514 term.clear_screen_home(true);
515 assert_eq!(term.cursor_row(), 0);
516
517 term.append("Test\ntext\na\nb\nc\n");
518 assert_eq!(term.cursor_row(), 5);
519 term.clear_to_color(Color::DarkBlue);
520 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521 assert_eq!(term.text_bg_color(), Color::TransparentBg);
522 assert_eq!(term.cursor_row(), 0);
523
524 // Test cursor_home()
525 term.append("Test\n\n\n\n\n\n\n\n\n\n");
526 assert_eq!(term.cursor_row(), 10);
527 term.cursor_home();
528 assert_eq!(term.cursor_row(), 0);
529
530 // Test the widget color
531 assert_eq!(term.color(), Color::Black); // Default
532 term.set_color(Color::DarkGreen);
533 assert_eq!(term.color(), Color::DarkGreen);
534 term.set_color(Color::Black);
535 assert_eq!(term.color(), Color::Black);
536 term.append(
537 "This should be one line of white text on black, embedded into the top of a blue field.\n",
538 );
539
540 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541 term.set_output_translate(OutFlags::OFF);
542 assert_eq!(term.output_translate(), OutFlags::OFF);
543 term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547 term.append("\nDone!\n");
548 term.set_text_attrib(Attrib::Normal);
549}
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?
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496 term.take_focus().unwrap();
497 term.reset_terminal();
498 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500 assert_eq!(term.history_use(), 0);
501 term.clear();
502 assert_eq!(term.cursor_row(), 0);
503 assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505 term.append("Test\ntext\na\nb\nc\nd");
506 assert_eq!(term.cursor_row(), 5);
507 let hist = term.history_use();
508 term.clear_screen_home(false);
509 assert_eq!(term.cursor_row(), 0);
510 assert_eq!(term.history_use(), hist); // History not changed
511
512 term.append("Test\ntext\na\nb\nc\nd\ne");
513 assert_eq!(term.cursor_row(), 6);
514 term.clear_screen_home(true);
515 assert_eq!(term.cursor_row(), 0);
516
517 term.append("Test\ntext\na\nb\nc\n");
518 assert_eq!(term.cursor_row(), 5);
519 term.clear_to_color(Color::DarkBlue);
520 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521 assert_eq!(term.text_bg_color(), Color::TransparentBg);
522 assert_eq!(term.cursor_row(), 0);
523
524 // Test cursor_home()
525 term.append("Test\n\n\n\n\n\n\n\n\n\n");
526 assert_eq!(term.cursor_row(), 10);
527 term.cursor_home();
528 assert_eq!(term.cursor_row(), 0);
529
530 // Test the widget color
531 assert_eq!(term.color(), Color::Black); // Default
532 term.set_color(Color::DarkGreen);
533 assert_eq!(term.color(), Color::DarkGreen);
534 term.set_color(Color::Black);
535 assert_eq!(term.color(), Color::Black);
536 term.append(
537 "This should be one line of white text on black, embedded into the top of a blue field.\n",
538 );
539
540 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541 term.set_output_translate(OutFlags::OFF);
542 assert_eq!(term.output_translate(), OutFlags::OFF);
543 term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547 term.append("\nDone!\n");
548 term.set_text_attrib(Attrib::Normal);
549}
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?
476fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
477 term.take_focus().unwrap();
478 term.reset_terminal();
479
480 for i in 0..50 {
481 term.append(&format!("{i}\n"));
482 }
483 assert_eq!(term.history_rows(), 100);
484
485 term.clear_history();
486 assert_eq!(term.history_use(), 0);
487
488 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
489 term.append("\nDone!\n");
490 term.set_text_attrib(Attrib::Normal);
491}
492
493//--------------------------------------------------------------------------------------
494/// Another set of tests that run when Test3 is clicked
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496 term.take_focus().unwrap();
497 term.reset_terminal();
498 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500 assert_eq!(term.history_use(), 0);
501 term.clear();
502 assert_eq!(term.cursor_row(), 0);
503 assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505 term.append("Test\ntext\na\nb\nc\nd");
506 assert_eq!(term.cursor_row(), 5);
507 let hist = term.history_use();
508 term.clear_screen_home(false);
509 assert_eq!(term.cursor_row(), 0);
510 assert_eq!(term.history_use(), hist); // History not changed
511
512 term.append("Test\ntext\na\nb\nc\nd\ne");
513 assert_eq!(term.cursor_row(), 6);
514 term.clear_screen_home(true);
515 assert_eq!(term.cursor_row(), 0);
516
517 term.append("Test\ntext\na\nb\nc\n");
518 assert_eq!(term.cursor_row(), 5);
519 term.clear_to_color(Color::DarkBlue);
520 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521 assert_eq!(term.text_bg_color(), Color::TransparentBg);
522 assert_eq!(term.cursor_row(), 0);
523
524 // Test cursor_home()
525 term.append("Test\n\n\n\n\n\n\n\n\n\n");
526 assert_eq!(term.cursor_row(), 10);
527 term.cursor_home();
528 assert_eq!(term.cursor_row(), 0);
529
530 // Test the widget color
531 assert_eq!(term.color(), Color::Black); // Default
532 term.set_color(Color::DarkGreen);
533 assert_eq!(term.color(), Color::DarkGreen);
534 term.set_color(Color::Black);
535 assert_eq!(term.color(), Color::Black);
536 term.append(
537 "This should be one line of white text on black, embedded into the top of a blue field.\n",
538 );
539
540 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541 term.set_output_translate(OutFlags::OFF);
542 assert_eq!(term.output_translate(), OutFlags::OFF);
543 term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547 term.append("\nDone!\n");
548 term.set_text_attrib(Attrib::Normal);
549}
550
551//--------------------------------------------------------------------------------------
552/// Another set of tests for the ring-buffer access methods
553/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555 let sel_len = term.selection_text_len();
556 let sel = term.selection_text();
557
558 term.take_focus().unwrap();
559 term.reset_terminal();
560 // Test the Utf8Char primitive
561 let uc = Utf8Char::new(b'Q');
562 let uc1 = uc.text_utf8();
563 assert_eq!(&uc1, b"Q");
564 assert_eq!(&uc.attrib(), &Attrib::Normal);
565 assert_eq!(
566 &uc.charflags(),
567 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568 );
569 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570 assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572 let ring_rows = term.ring_rows();
573
574 term.take_focus().unwrap();
575 term.clear_history();
576 assert_eq!(term.history_use(), 0);
577
578 // Subtract row numbers, modulo `rows`
579 fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580 match a - b {
581 n if n < 0 => n + rows,
582 n => n,
583 }
584 }
585 // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587 assert!(term.disp_srow() >= 0);
588 assert!(term.disp_erow() >= 0);
589 assert!(term.hist_srow() >= 0);
590 assert!(term.hist_erow() >= 0);
591 assert!(term.offset() >= 0);
592 assert!(term.disp_srow() <= ring_rows);
593 assert!(term.disp_erow() <= ring_rows);
594 assert!(term.hist_srow() <= ring_rows);
595 assert!(term.hist_erow() <= ring_rows);
596 assert!(term.offset() <= ring_rows);
597
598 assert_eq!(term.ring_srow(), 0);
599 assert_eq!(term.ring_erow(), ring_rows - 1);
600 assert_eq!(
601 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602 term.display_rows()
603 );
604 assert_eq!(
605 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606 term.history_rows()
607 );
608
609 assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610 assert_eq!(term.ring_srow(), 0);
611
612 /// Local function to read back all rows from the display into a long string.
613 /// Does not include scrollback history.
614 /// Trims trailing blanks on each line
615 fn read_disp(term: &Terminal) -> String {
616 let rows = term.display_rows();
617 let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618 for row in 0..rows {
619 let r = term.u8c_disp_row(row).trim();
620 // Iterate through a row, accumulating [u8]
621 for c in r.iter() {
622 // Note: Sometimes utf-8 length is > 1
623 text.extend_from_slice(c.text_utf8());
624 }
625 text.extend_from_slice(b"\n");
626 }
627 // Return the result as a string
628 std::str::from_utf8(&text).unwrap().to_string()
629 }
630
631 term.clear();
632 term.append("Top line ↑ (up-arrow)");
633 term.set_text_attrib(Attrib::Underline);
634 term.append(" ");
635 term.set_text_attrib(Attrib::Normal);
636 term.append(" \n");
637 let mut text_out = read_disp(term);
638 // Trim trailing empty lines
639 text_out = text_out.trim_end_matches(&"\n").to_string();
640 // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642 assert_eq!(text_out, "Top line ↑ (up-arrow) ");
643 let r = term.u8c_disp_row(0);
644 assert_eq!(r.col(0).text_utf8(), b"T");
645 assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646 assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647 let r = term.u8c_disp_row(1);
648 assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649 assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651 // Clear the screen again, then append test text, then read it back and compare
652 let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656 Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659 term.clear_history();
660 term.clear();
661 let bg_save = term.text_bg_color();
662 let fg_save = term.text_fg_color();
663 term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664 term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665 term.append(test_text);
666 term.set_text_bg_color(bg_save);
667 term.set_text_fg_color(fg_save);
668
669 let mut text_out = read_disp(term);
670 // Trim trailing empty lines
671 text_out = text_out.trim_end_matches(&"\n").to_string();
672 assert_eq!(test_text, text_out);
673
674 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676 assert_eq!(term.ring_srow(), 0);
677 assert_eq!(term.ring_erow(), ring_rows - 1);
678 assert_eq!(
679 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680 term.display_rows()
681 );
682 assert_eq!(
683 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684 term.history_rows()
685 );
686
687 term.append(&format!(
688 "\n\nScreen has {} rows of {} columns.\n",
689 term.display_rows(),
690 term.display_columns()
691 ));
692
693 term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
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?
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496 term.take_focus().unwrap();
497 term.reset_terminal();
498 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500 assert_eq!(term.history_use(), 0);
501 term.clear();
502 assert_eq!(term.cursor_row(), 0);
503 assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505 term.append("Test\ntext\na\nb\nc\nd");
506 assert_eq!(term.cursor_row(), 5);
507 let hist = term.history_use();
508 term.clear_screen_home(false);
509 assert_eq!(term.cursor_row(), 0);
510 assert_eq!(term.history_use(), hist); // History not changed
511
512 term.append("Test\ntext\na\nb\nc\nd\ne");
513 assert_eq!(term.cursor_row(), 6);
514 term.clear_screen_home(true);
515 assert_eq!(term.cursor_row(), 0);
516
517 term.append("Test\ntext\na\nb\nc\n");
518 assert_eq!(term.cursor_row(), 5);
519 term.clear_to_color(Color::DarkBlue);
520 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521 assert_eq!(term.text_bg_color(), Color::TransparentBg);
522 assert_eq!(term.cursor_row(), 0);
523
524 // Test cursor_home()
525 term.append("Test\n\n\n\n\n\n\n\n\n\n");
526 assert_eq!(term.cursor_row(), 10);
527 term.cursor_home();
528 assert_eq!(term.cursor_row(), 0);
529
530 // Test the widget color
531 assert_eq!(term.color(), Color::Black); // Default
532 term.set_color(Color::DarkGreen);
533 assert_eq!(term.color(), Color::DarkGreen);
534 term.set_color(Color::Black);
535 assert_eq!(term.color(), Color::Black);
536 term.append(
537 "This should be one line of white text on black, embedded into the top of a blue field.\n",
538 );
539
540 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541 term.set_output_translate(OutFlags::OFF);
542 assert_eq!(term.output_translate(), OutFlags::OFF);
543 term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547 term.append("\nDone!\n");
548 term.set_text_attrib(Attrib::Normal);
549}
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?
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496 term.take_focus().unwrap();
497 term.reset_terminal();
498 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500 assert_eq!(term.history_use(), 0);
501 term.clear();
502 assert_eq!(term.cursor_row(), 0);
503 assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505 term.append("Test\ntext\na\nb\nc\nd");
506 assert_eq!(term.cursor_row(), 5);
507 let hist = term.history_use();
508 term.clear_screen_home(false);
509 assert_eq!(term.cursor_row(), 0);
510 assert_eq!(term.history_use(), hist); // History not changed
511
512 term.append("Test\ntext\na\nb\nc\nd\ne");
513 assert_eq!(term.cursor_row(), 6);
514 term.clear_screen_home(true);
515 assert_eq!(term.cursor_row(), 0);
516
517 term.append("Test\ntext\na\nb\nc\n");
518 assert_eq!(term.cursor_row(), 5);
519 term.clear_to_color(Color::DarkBlue);
520 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521 assert_eq!(term.text_bg_color(), Color::TransparentBg);
522 assert_eq!(term.cursor_row(), 0);
523
524 // Test cursor_home()
525 term.append("Test\n\n\n\n\n\n\n\n\n\n");
526 assert_eq!(term.cursor_row(), 10);
527 term.cursor_home();
528 assert_eq!(term.cursor_row(), 0);
529
530 // Test the widget color
531 assert_eq!(term.color(), Color::Black); // Default
532 term.set_color(Color::DarkGreen);
533 assert_eq!(term.color(), Color::DarkGreen);
534 term.set_color(Color::Black);
535 assert_eq!(term.color(), Color::Black);
536 term.append(
537 "This should be one line of white text on black, embedded into the top of a blue field.\n",
538 );
539
540 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541 term.set_output_translate(OutFlags::OFF);
542 assert_eq!(term.output_translate(), OutFlags::OFF);
543 term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547 term.append("\nDone!\n");
548 term.set_text_attrib(Attrib::Normal);
549}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
425//--------------------------------------------------------------------------------------
426/// More tests that run when the menu bar Test1 is clicked
427fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
428 term.take_focus().unwrap();
429 term.reset_terminal();
430 term.append("0123456789 0\n");
431 term.append("0123456789 1\n");
432 term.append("0123456789 2\n");
433 term.append("0123456789 3\n");
434 term.append("0123456789 4\n");
435 term.append("0123456789 5\n");
436 term.append("0123456789 6\n");
437 term.append("0123456789 7\n");
438 term.append("0123456789 8\n");
439 term.append("0123456789 9\n");
440 term.append("------------\n");
441
442 term.set_text_fg_color(Color::Green);
443 term.plot_char('A', 0, 0);
444 term.plot_char('B', 1, 1);
445 term.plot_char('C', 2, 2);
446 term.plot_char('D', 3, 3);
447 term.plot_char('E', 4, 4);
448 term.plot_char('F', 5, 5);
449 term.set_text_fg_color(Color::XtermWhite);
450
451 assert_eq!(term.cursor_row(), 11);
452 assert_eq!(term.cursor_col(), 0);
453
454 term.set_text_bg_color(Color::DarkBlue);
455 term.plot_char_utf8('b', 8, 1);
456 term.plot_char_utf8('↑', 9, 1);
457 term.plot_char_utf8('c', 8, 2);
458 term.plot_char_utf8('↑', 9, 2);
459 term.plot_char_utf8('d', 8, 3);
460 term.plot_char_utf8('↑', 9, 3);
461 term.plot_char_utf8('e', 8, 4);
462 term.plot_char_utf8('↑', 9, 4);
463 term.plot_char_utf8('f', 8, 5);
464 term.plot_char_utf8('↑', 9, 5);
465 term.plot_char_utf8('g', 8, 6);
466 term.plot_char_utf8('↑', 9, 6);
467 term.set_text_bg_color(Color::TransparentBg);
468
469 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
470 term.append("Done!\n");
471 term.set_text_attrib(Attrib::Normal);
472}
473
474//--------------------------------------------------------------------------------------
475/// More tests that run when the menu bar button Test2 is clicked
476fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
477 term.take_focus().unwrap();
478 term.reset_terminal();
479
480 for i in 0..50 {
481 term.append(&format!("{i}\n"));
482 }
483 assert_eq!(term.history_rows(), 100);
484
485 term.clear_history();
486 assert_eq!(term.history_use(), 0);
487
488 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
489 term.append("\nDone!\n");
490 term.set_text_attrib(Attrib::Normal);
491}
492
493//--------------------------------------------------------------------------------------
494/// Another set of tests that run when Test3 is clicked
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496 term.take_focus().unwrap();
497 term.reset_terminal();
498 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500 assert_eq!(term.history_use(), 0);
501 term.clear();
502 assert_eq!(term.cursor_row(), 0);
503 assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505 term.append("Test\ntext\na\nb\nc\nd");
506 assert_eq!(term.cursor_row(), 5);
507 let hist = term.history_use();
508 term.clear_screen_home(false);
509 assert_eq!(term.cursor_row(), 0);
510 assert_eq!(term.history_use(), hist); // History not changed
511
512 term.append("Test\ntext\na\nb\nc\nd\ne");
513 assert_eq!(term.cursor_row(), 6);
514 term.clear_screen_home(true);
515 assert_eq!(term.cursor_row(), 0);
516
517 term.append("Test\ntext\na\nb\nc\n");
518 assert_eq!(term.cursor_row(), 5);
519 term.clear_to_color(Color::DarkBlue);
520 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521 assert_eq!(term.text_bg_color(), Color::TransparentBg);
522 assert_eq!(term.cursor_row(), 0);
523
524 // Test cursor_home()
525 term.append("Test\n\n\n\n\n\n\n\n\n\n");
526 assert_eq!(term.cursor_row(), 10);
527 term.cursor_home();
528 assert_eq!(term.cursor_row(), 0);
529
530 // Test the widget color
531 assert_eq!(term.color(), Color::Black); // Default
532 term.set_color(Color::DarkGreen);
533 assert_eq!(term.color(), Color::DarkGreen);
534 term.set_color(Color::Black);
535 assert_eq!(term.color(), Color::Black);
536 term.append(
537 "This should be one line of white text on black, embedded into the top of a blue field.\n",
538 );
539
540 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541 term.set_output_translate(OutFlags::OFF);
542 assert_eq!(term.output_translate(), OutFlags::OFF);
543 term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547 term.append("\nDone!\n");
548 term.set_text_attrib(Attrib::Normal);
549}
550
551//--------------------------------------------------------------------------------------
552/// Another set of tests for the ring-buffer access methods
553/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555 let sel_len = term.selection_text_len();
556 let sel = term.selection_text();
557
558 term.take_focus().unwrap();
559 term.reset_terminal();
560 // Test the Utf8Char primitive
561 let uc = Utf8Char::new(b'Q');
562 let uc1 = uc.text_utf8();
563 assert_eq!(&uc1, b"Q");
564 assert_eq!(&uc.attrib(), &Attrib::Normal);
565 assert_eq!(
566 &uc.charflags(),
567 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568 );
569 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570 assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572 let ring_rows = term.ring_rows();
573
574 term.take_focus().unwrap();
575 term.clear_history();
576 assert_eq!(term.history_use(), 0);
577
578 // Subtract row numbers, modulo `rows`
579 fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580 match a - b {
581 n if n < 0 => n + rows,
582 n => n,
583 }
584 }
585 // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587 assert!(term.disp_srow() >= 0);
588 assert!(term.disp_erow() >= 0);
589 assert!(term.hist_srow() >= 0);
590 assert!(term.hist_erow() >= 0);
591 assert!(term.offset() >= 0);
592 assert!(term.disp_srow() <= ring_rows);
593 assert!(term.disp_erow() <= ring_rows);
594 assert!(term.hist_srow() <= ring_rows);
595 assert!(term.hist_erow() <= ring_rows);
596 assert!(term.offset() <= ring_rows);
597
598 assert_eq!(term.ring_srow(), 0);
599 assert_eq!(term.ring_erow(), ring_rows - 1);
600 assert_eq!(
601 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602 term.display_rows()
603 );
604 assert_eq!(
605 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606 term.history_rows()
607 );
608
609 assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610 assert_eq!(term.ring_srow(), 0);
611
612 /// Local function to read back all rows from the display into a long string.
613 /// Does not include scrollback history.
614 /// Trims trailing blanks on each line
615 fn read_disp(term: &Terminal) -> String {
616 let rows = term.display_rows();
617 let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618 for row in 0..rows {
619 let r = term.u8c_disp_row(row).trim();
620 // Iterate through a row, accumulating [u8]
621 for c in r.iter() {
622 // Note: Sometimes utf-8 length is > 1
623 text.extend_from_slice(c.text_utf8());
624 }
625 text.extend_from_slice(b"\n");
626 }
627 // Return the result as a string
628 std::str::from_utf8(&text).unwrap().to_string()
629 }
630
631 term.clear();
632 term.append("Top line ↑ (up-arrow)");
633 term.set_text_attrib(Attrib::Underline);
634 term.append(" ");
635 term.set_text_attrib(Attrib::Normal);
636 term.append(" \n");
637 let mut text_out = read_disp(term);
638 // Trim trailing empty lines
639 text_out = text_out.trim_end_matches(&"\n").to_string();
640 // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642 assert_eq!(text_out, "Top line ↑ (up-arrow) ");
643 let r = term.u8c_disp_row(0);
644 assert_eq!(r.col(0).text_utf8(), b"T");
645 assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646 assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647 let r = term.u8c_disp_row(1);
648 assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649 assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651 // Clear the screen again, then append test text, then read it back and compare
652 let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656 Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659 term.clear_history();
660 term.clear();
661 let bg_save = term.text_bg_color();
662 let fg_save = term.text_fg_color();
663 term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664 term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665 term.append(test_text);
666 term.set_text_bg_color(bg_save);
667 term.set_text_fg_color(fg_save);
668
669 let mut text_out = read_disp(term);
670 // Trim trailing empty lines
671 text_out = text_out.trim_end_matches(&"\n").to_string();
672 assert_eq!(test_text, text_out);
673
674 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676 assert_eq!(term.ring_srow(), 0);
677 assert_eq!(term.ring_erow(), ring_rows - 1);
678 assert_eq!(
679 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680 term.display_rows()
681 );
682 assert_eq!(
683 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684 term.history_rows()
685 );
686
687 term.append(&format!(
688 "\n\nScreen has {} rows of {} columns.\n",
689 term.display_rows(),
690 term.display_columns()
691 ));
692
693 term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
695
696//--------------------------------------------------------------------------------------
697/// Yet another set of tests for misc cursor functions and other stuff
698/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700 term.take_focus().unwrap();
701
702 // Test the attr_fg_color and attr_bg_color methods.
703 // Put a single character 'A' into the buffer and check it
704 term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705 term.set_text_bg_color(Color::TransparentBg);
706 term.set_text_fg_color(Color::XtermWhite);
707 term.append("A");
708 let r = &term.u8c_disp_row(0);
709 let uc = r.col(0);
710 assert_eq!(uc.text_utf8(), b"A");
711 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712 assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713 assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714 assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715 assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717 // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718 term.clear();
719 term.set_text_fg_color_xterm(XtermColor::White);
720 term.set_text_bg_color_xterm(XtermColor::Black);
721 assert_eq!(term.text_attrib(), Attrib::Normal);
722
723 assert!(term.ansi());
724 term.append("B\x1b[32mC\x1b[1mD\n");
725
726 let r = &term.u8c_disp_row(0);
727 let uc = r.col(0);
728 assert_eq!(uc.text_utf8(), b"B");
729 assert!(uc.is_char(b'B'));
730 assert!(!uc.is_char(b'A'));
731 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735 assert_eq!(
736 &uc.charflags(),
737 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738 );
739
740 let uc = r.col(1);
741 assert_eq!(uc.text_utf8(), b"C");
742 assert!(uc.is_char(b'C'));
743 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747 assert_eq!(
748 &uc.charflags(),
749 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750 );
751
752 let uc = r.col(2);
753 assert_eq!(uc.text_utf8(), b"D");
754 assert!(uc.is_char(b'D'));
755 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757 assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759 assert_eq!(
760 &uc.attr_fgcolor(Some(term)),
761 &Color::from_rgb(0x20, 0xf0, 0x20)
762 );
763 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764 assert_eq!(
765 &uc.charflags(),
766 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767 );
768
769 // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770 term.clear();
771 term.set_text_fg_color_xterm(XtermColor::White);
772 term.set_text_bg_color_xterm(XtermColor::Black);
773 term.set_text_attrib(Attrib::Normal);
774 assert_eq!(term.text_attrib(), Attrib::Normal);
775
776 assert!(term.ansi());
777 term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779 let r = &term.u8c_disp_row(0);
780 let uc = r.col(0);
781 assert_eq!(uc.text_utf8(), b"B");
782 assert!(uc.is_char(b'B'));
783 assert!(!uc.is_char(b'A'));
784 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788 assert_eq!(
789 &uc.charflags(),
790 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791 );
792
793 let uc = r.col(1);
794 assert_eq!(uc.text_utf8(), b"C");
795 assert!(uc.is_char(b'C'));
796 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800 assert_eq!(
801 &uc.charflags(),
802 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803 );
804
805 let uc = r.col(2);
806 assert_eq!(uc.text_utf8(), b"D");
807 assert!(uc.is_char(b'D'));
808 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812 assert_eq!(
813 &uc.charflags(),
814 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815 );
816
817 let uc = r.col(3);
818 assert_eq!(uc.text_utf8(), b"E");
819 assert!(uc.is_char(b'E'));
820 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822 assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823 assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824 assert_eq!(
825 &uc.charflags(),
826 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827 );
828
829 // Test some miscellaneous Utf8 constants
830 assert_eq!(uc.length(), 1);
831 assert_eq!(uc.max_utf8(), 4);
832 assert_eq!(uc.pwidth(), 9.0);
833 assert_eq!(uc.pwidth_int(), 9);
834
835 term.set_text_fg_color_xterm(XtermColor::White);
836 term.set_text_bg_color_xterm(XtermColor::Black);
837 term.clear();
838 term.set_text_attrib(Attrib::Normal);
839
840 // Mouse selection functions
841 term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842 term.clear_mouse_selection();
843 assert_eq!(term.get_selection(), None);
844
845 // Play with cursor position
846 term.append("0123456789\n"); // Set up test pattern
847 term.append("ABCDEFGHIJ\n");
848 term.append("abcdefghij\n");
849
850 term.set_cursor_row(1);
851 assert_eq!(term.cursor_row(), 1);
852 term.set_cursor_col(1);
853 assert_eq!(term.cursor_col(), 1);
854 assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856 term.append("----"); // Overwrites text at cursor and moves cursor forward
857 assert_eq!(term.cursor_row(), 1);
858 assert_eq!(term.cursor_col(), 5);
859 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860 term.set_cursor_col(1);
861 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863 term.cursor_up(1, false);
864 assert_eq!(term.cursor_row(), 0);
865 assert_eq!(term.cursor_col(), 1);
866 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868 // Hit top of screen, so nothing happens
869 term.cursor_up(1, false);
870 assert_eq!(term.cursor_row(), 0);
871 assert_eq!(term.cursor_col(), 1);
872 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874 // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875 term.cursor_up(1, true);
876 assert_eq!(term.cursor_row(), 0);
877 assert_eq!(term.cursor_col(), 1);
878 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880 // Go back down to the overwritten text
881 term.cursor_down(2, false);
882 assert_eq!(term.cursor_row(), 2);
883 assert_eq!(term.cursor_col(), 1);
884 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886 // Go right past the overwritten text
887 term.cursor_right(4, false);
888 assert_eq!(term.cursor_row(), 2);
889 assert_eq!(term.cursor_col(), 5);
890 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892 // Go left to the end of the overwritten text
893 term.cursor_left(1);
894 assert_eq!(term.cursor_row(), 2);
895 assert_eq!(term.cursor_col(), 4);
896 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898 // Scroll back down, removing the blank line at the top.
899 // Cursor stays in place, the text moves under it.
900 term.scroll(1);
901 assert_eq!(term.cursor_row(), 2);
902 assert_eq!(term.cursor_col(), 4);
903 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905 // Clear from here to end-of-line
906 term.clear_eol();
907 assert_eq!(term.cursor_row(), 2);
908 assert_eq!(term.cursor_col(), 4);
909 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911 // Now clear from here to start-of-line. Cursor does not move.
912 term.clear_sol();
913 assert_eq!(term.cursor_row(), 2);
914 assert_eq!(term.cursor_col(), 4);
915 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916 term.cursor_left(1);
917 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918 term.set_cursor_col(0);
919 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921 // Clear some lines
922 term.clear_line(1);
923 assert_eq!(term.cursor_row(), 2);
924 assert_eq!(term.cursor_col(), 0);
925 term.set_cursor_row(1);
926 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927 term.set_cursor_row(3);
928 term.clear_cur_line();
929 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930 assert_eq!(term.cursor_row(), 3);
931 assert_eq!(term.cursor_col(), 0);
932
933 term.append("Two lines above are intentionally left blank.\n");
934 assert_eq!(term.cursor_row(), 4);
935 assert_eq!(term.cursor_col(), 0);
936
937 // Set up the test pattern again, then play with insert/delete
938 term.append("0123456789\n");
939 term.append("ABCDEFGHIJ\n");
940 term.append("abcdefghij\n");
941 assert_eq!(term.cursor_row(), 7);
942
943 term.set_cursor_row(4);
944 term.set_cursor_col(4);
945 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947 term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949 term.cursor_right(5, false);
950 assert_eq!(term.cursor_col(), 9);
951 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953 // Insert two blank rows above cursor. Cursor stays put.
954 term.insert_rows(2);
955 assert_eq!(term.cursor_row(), 4);
956 assert_eq!(term.cursor_col(), 9);
957 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958 term.cursor_down(2, false); // Go down to find our text again
959 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961 // Go back to the beginning of the inserted 'x' characters and delete them.
962 term.cursor_left(5);
963 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964 term.delete_cur_chars(5);
965 assert_eq!(term.cursor_row(), 6);
966 assert_eq!(term.cursor_col(), 4);
967 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969 term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970 term.cursor_down(1, false);
971 term.cursor_left(2);
972 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974 term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975 assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976 term.cursor_up(1, false);
977 term.delete_rows(2); // Delete remains of test pattern
978
979 term.set_text_attrib(Attrib::Bold);
980 term.insert_char_eol('-', 3, 15, 20);
981 term.set_cursor_row(3);
982 term.set_cursor_col(15);
983 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984 assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986 term.set_text_attrib(Attrib::Italic);
987 term.append(" and all lines below");
988 term.set_text_attrib(Attrib::Normal);
989 term.cursor_down(1, false);
990
991 let mut hsb = term.hscrollbar();
992 let mut sb = term.scrollbar();
993 hsb.set_value(100.0);
994 assert_eq!(hsb.value(), 100.0);
995 sb.set_value(50.0);
996 assert_eq!(sb.value(), 50.0);
997 hsb.set_value(0.0);
998 assert_eq!(hsb.value(), 0.0);
999 sb.set_value(0.0);
1000 assert_eq!(sb.value(), 0.0);
1001}
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?
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700 term.take_focus().unwrap();
701
702 // Test the attr_fg_color and attr_bg_color methods.
703 // Put a single character 'A' into the buffer and check it
704 term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705 term.set_text_bg_color(Color::TransparentBg);
706 term.set_text_fg_color(Color::XtermWhite);
707 term.append("A");
708 let r = &term.u8c_disp_row(0);
709 let uc = r.col(0);
710 assert_eq!(uc.text_utf8(), b"A");
711 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712 assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713 assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714 assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715 assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717 // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718 term.clear();
719 term.set_text_fg_color_xterm(XtermColor::White);
720 term.set_text_bg_color_xterm(XtermColor::Black);
721 assert_eq!(term.text_attrib(), Attrib::Normal);
722
723 assert!(term.ansi());
724 term.append("B\x1b[32mC\x1b[1mD\n");
725
726 let r = &term.u8c_disp_row(0);
727 let uc = r.col(0);
728 assert_eq!(uc.text_utf8(), b"B");
729 assert!(uc.is_char(b'B'));
730 assert!(!uc.is_char(b'A'));
731 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735 assert_eq!(
736 &uc.charflags(),
737 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738 );
739
740 let uc = r.col(1);
741 assert_eq!(uc.text_utf8(), b"C");
742 assert!(uc.is_char(b'C'));
743 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747 assert_eq!(
748 &uc.charflags(),
749 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750 );
751
752 let uc = r.col(2);
753 assert_eq!(uc.text_utf8(), b"D");
754 assert!(uc.is_char(b'D'));
755 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757 assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759 assert_eq!(
760 &uc.attr_fgcolor(Some(term)),
761 &Color::from_rgb(0x20, 0xf0, 0x20)
762 );
763 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764 assert_eq!(
765 &uc.charflags(),
766 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767 );
768
769 // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770 term.clear();
771 term.set_text_fg_color_xterm(XtermColor::White);
772 term.set_text_bg_color_xterm(XtermColor::Black);
773 term.set_text_attrib(Attrib::Normal);
774 assert_eq!(term.text_attrib(), Attrib::Normal);
775
776 assert!(term.ansi());
777 term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779 let r = &term.u8c_disp_row(0);
780 let uc = r.col(0);
781 assert_eq!(uc.text_utf8(), b"B");
782 assert!(uc.is_char(b'B'));
783 assert!(!uc.is_char(b'A'));
784 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788 assert_eq!(
789 &uc.charflags(),
790 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791 );
792
793 let uc = r.col(1);
794 assert_eq!(uc.text_utf8(), b"C");
795 assert!(uc.is_char(b'C'));
796 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800 assert_eq!(
801 &uc.charflags(),
802 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803 );
804
805 let uc = r.col(2);
806 assert_eq!(uc.text_utf8(), b"D");
807 assert!(uc.is_char(b'D'));
808 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812 assert_eq!(
813 &uc.charflags(),
814 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815 );
816
817 let uc = r.col(3);
818 assert_eq!(uc.text_utf8(), b"E");
819 assert!(uc.is_char(b'E'));
820 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822 assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823 assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824 assert_eq!(
825 &uc.charflags(),
826 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827 );
828
829 // Test some miscellaneous Utf8 constants
830 assert_eq!(uc.length(), 1);
831 assert_eq!(uc.max_utf8(), 4);
832 assert_eq!(uc.pwidth(), 9.0);
833 assert_eq!(uc.pwidth_int(), 9);
834
835 term.set_text_fg_color_xterm(XtermColor::White);
836 term.set_text_bg_color_xterm(XtermColor::Black);
837 term.clear();
838 term.set_text_attrib(Attrib::Normal);
839
840 // Mouse selection functions
841 term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842 term.clear_mouse_selection();
843 assert_eq!(term.get_selection(), None);
844
845 // Play with cursor position
846 term.append("0123456789\n"); // Set up test pattern
847 term.append("ABCDEFGHIJ\n");
848 term.append("abcdefghij\n");
849
850 term.set_cursor_row(1);
851 assert_eq!(term.cursor_row(), 1);
852 term.set_cursor_col(1);
853 assert_eq!(term.cursor_col(), 1);
854 assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856 term.append("----"); // Overwrites text at cursor and moves cursor forward
857 assert_eq!(term.cursor_row(), 1);
858 assert_eq!(term.cursor_col(), 5);
859 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860 term.set_cursor_col(1);
861 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863 term.cursor_up(1, false);
864 assert_eq!(term.cursor_row(), 0);
865 assert_eq!(term.cursor_col(), 1);
866 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868 // Hit top of screen, so nothing happens
869 term.cursor_up(1, false);
870 assert_eq!(term.cursor_row(), 0);
871 assert_eq!(term.cursor_col(), 1);
872 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874 // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875 term.cursor_up(1, true);
876 assert_eq!(term.cursor_row(), 0);
877 assert_eq!(term.cursor_col(), 1);
878 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880 // Go back down to the overwritten text
881 term.cursor_down(2, false);
882 assert_eq!(term.cursor_row(), 2);
883 assert_eq!(term.cursor_col(), 1);
884 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886 // Go right past the overwritten text
887 term.cursor_right(4, false);
888 assert_eq!(term.cursor_row(), 2);
889 assert_eq!(term.cursor_col(), 5);
890 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892 // Go left to the end of the overwritten text
893 term.cursor_left(1);
894 assert_eq!(term.cursor_row(), 2);
895 assert_eq!(term.cursor_col(), 4);
896 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898 // Scroll back down, removing the blank line at the top.
899 // Cursor stays in place, the text moves under it.
900 term.scroll(1);
901 assert_eq!(term.cursor_row(), 2);
902 assert_eq!(term.cursor_col(), 4);
903 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905 // Clear from here to end-of-line
906 term.clear_eol();
907 assert_eq!(term.cursor_row(), 2);
908 assert_eq!(term.cursor_col(), 4);
909 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911 // Now clear from here to start-of-line. Cursor does not move.
912 term.clear_sol();
913 assert_eq!(term.cursor_row(), 2);
914 assert_eq!(term.cursor_col(), 4);
915 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916 term.cursor_left(1);
917 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918 term.set_cursor_col(0);
919 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921 // Clear some lines
922 term.clear_line(1);
923 assert_eq!(term.cursor_row(), 2);
924 assert_eq!(term.cursor_col(), 0);
925 term.set_cursor_row(1);
926 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927 term.set_cursor_row(3);
928 term.clear_cur_line();
929 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930 assert_eq!(term.cursor_row(), 3);
931 assert_eq!(term.cursor_col(), 0);
932
933 term.append("Two lines above are intentionally left blank.\n");
934 assert_eq!(term.cursor_row(), 4);
935 assert_eq!(term.cursor_col(), 0);
936
937 // Set up the test pattern again, then play with insert/delete
938 term.append("0123456789\n");
939 term.append("ABCDEFGHIJ\n");
940 term.append("abcdefghij\n");
941 assert_eq!(term.cursor_row(), 7);
942
943 term.set_cursor_row(4);
944 term.set_cursor_col(4);
945 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947 term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949 term.cursor_right(5, false);
950 assert_eq!(term.cursor_col(), 9);
951 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953 // Insert two blank rows above cursor. Cursor stays put.
954 term.insert_rows(2);
955 assert_eq!(term.cursor_row(), 4);
956 assert_eq!(term.cursor_col(), 9);
957 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958 term.cursor_down(2, false); // Go down to find our text again
959 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961 // Go back to the beginning of the inserted 'x' characters and delete them.
962 term.cursor_left(5);
963 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964 term.delete_cur_chars(5);
965 assert_eq!(term.cursor_row(), 6);
966 assert_eq!(term.cursor_col(), 4);
967 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969 term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970 term.cursor_down(1, false);
971 term.cursor_left(2);
972 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974 term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975 assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976 term.cursor_up(1, false);
977 term.delete_rows(2); // Delete remains of test pattern
978
979 term.set_text_attrib(Attrib::Bold);
980 term.insert_char_eol('-', 3, 15, 20);
981 term.set_cursor_row(3);
982 term.set_cursor_col(15);
983 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984 assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986 term.set_text_attrib(Attrib::Italic);
987 term.append(" and all lines below");
988 term.set_text_attrib(Attrib::Normal);
989 term.cursor_down(1, false);
990
991 let mut hsb = term.hscrollbar();
992 let mut sb = term.scrollbar();
993 hsb.set_value(100.0);
994 assert_eq!(hsb.value(), 100.0);
995 sb.set_value(50.0);
996 assert_eq!(sb.value(), 50.0);
997 hsb.set_value(0.0);
998 assert_eq!(hsb.value(), 0.0);
999 sb.set_value(0.0);
1000 assert_eq!(sb.value(), 0.0);
1001}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
425//--------------------------------------------------------------------------------------
426/// More tests that run when the menu bar Test1 is clicked
427fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
428 term.take_focus().unwrap();
429 term.reset_terminal();
430 term.append("0123456789 0\n");
431 term.append("0123456789 1\n");
432 term.append("0123456789 2\n");
433 term.append("0123456789 3\n");
434 term.append("0123456789 4\n");
435 term.append("0123456789 5\n");
436 term.append("0123456789 6\n");
437 term.append("0123456789 7\n");
438 term.append("0123456789 8\n");
439 term.append("0123456789 9\n");
440 term.append("------------\n");
441
442 term.set_text_fg_color(Color::Green);
443 term.plot_char('A', 0, 0);
444 term.plot_char('B', 1, 1);
445 term.plot_char('C', 2, 2);
446 term.plot_char('D', 3, 3);
447 term.plot_char('E', 4, 4);
448 term.plot_char('F', 5, 5);
449 term.set_text_fg_color(Color::XtermWhite);
450
451 assert_eq!(term.cursor_row(), 11);
452 assert_eq!(term.cursor_col(), 0);
453
454 term.set_text_bg_color(Color::DarkBlue);
455 term.plot_char_utf8('b', 8, 1);
456 term.plot_char_utf8('↑', 9, 1);
457 term.plot_char_utf8('c', 8, 2);
458 term.plot_char_utf8('↑', 9, 2);
459 term.plot_char_utf8('d', 8, 3);
460 term.plot_char_utf8('↑', 9, 3);
461 term.plot_char_utf8('e', 8, 4);
462 term.plot_char_utf8('↑', 9, 4);
463 term.plot_char_utf8('f', 8, 5);
464 term.plot_char_utf8('↑', 9, 5);
465 term.plot_char_utf8('g', 8, 6);
466 term.plot_char_utf8('↑', 9, 6);
467 term.set_text_bg_color(Color::TransparentBg);
468
469 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
470 term.append("Done!\n");
471 term.set_text_attrib(Attrib::Normal);
472}
473
474//--------------------------------------------------------------------------------------
475/// More tests that run when the menu bar button Test2 is clicked
476fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
477 term.take_focus().unwrap();
478 term.reset_terminal();
479
480 for i in 0..50 {
481 term.append(&format!("{i}\n"));
482 }
483 assert_eq!(term.history_rows(), 100);
484
485 term.clear_history();
486 assert_eq!(term.history_use(), 0);
487
488 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
489 term.append("\nDone!\n");
490 term.set_text_attrib(Attrib::Normal);
491}
492
493//--------------------------------------------------------------------------------------
494/// Another set of tests that run when Test3 is clicked
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496 term.take_focus().unwrap();
497 term.reset_terminal();
498 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500 assert_eq!(term.history_use(), 0);
501 term.clear();
502 assert_eq!(term.cursor_row(), 0);
503 assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505 term.append("Test\ntext\na\nb\nc\nd");
506 assert_eq!(term.cursor_row(), 5);
507 let hist = term.history_use();
508 term.clear_screen_home(false);
509 assert_eq!(term.cursor_row(), 0);
510 assert_eq!(term.history_use(), hist); // History not changed
511
512 term.append("Test\ntext\na\nb\nc\nd\ne");
513 assert_eq!(term.cursor_row(), 6);
514 term.clear_screen_home(true);
515 assert_eq!(term.cursor_row(), 0);
516
517 term.append("Test\ntext\na\nb\nc\n");
518 assert_eq!(term.cursor_row(), 5);
519 term.clear_to_color(Color::DarkBlue);
520 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521 assert_eq!(term.text_bg_color(), Color::TransparentBg);
522 assert_eq!(term.cursor_row(), 0);
523
524 // Test cursor_home()
525 term.append("Test\n\n\n\n\n\n\n\n\n\n");
526 assert_eq!(term.cursor_row(), 10);
527 term.cursor_home();
528 assert_eq!(term.cursor_row(), 0);
529
530 // Test the widget color
531 assert_eq!(term.color(), Color::Black); // Default
532 term.set_color(Color::DarkGreen);
533 assert_eq!(term.color(), Color::DarkGreen);
534 term.set_color(Color::Black);
535 assert_eq!(term.color(), Color::Black);
536 term.append(
537 "This should be one line of white text on black, embedded into the top of a blue field.\n",
538 );
539
540 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541 term.set_output_translate(OutFlags::OFF);
542 assert_eq!(term.output_translate(), OutFlags::OFF);
543 term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547 term.append("\nDone!\n");
548 term.set_text_attrib(Attrib::Normal);
549}
550
551//--------------------------------------------------------------------------------------
552/// Another set of tests for the ring-buffer access methods
553/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555 let sel_len = term.selection_text_len();
556 let sel = term.selection_text();
557
558 term.take_focus().unwrap();
559 term.reset_terminal();
560 // Test the Utf8Char primitive
561 let uc = Utf8Char::new(b'Q');
562 let uc1 = uc.text_utf8();
563 assert_eq!(&uc1, b"Q");
564 assert_eq!(&uc.attrib(), &Attrib::Normal);
565 assert_eq!(
566 &uc.charflags(),
567 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568 );
569 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570 assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572 let ring_rows = term.ring_rows();
573
574 term.take_focus().unwrap();
575 term.clear_history();
576 assert_eq!(term.history_use(), 0);
577
578 // Subtract row numbers, modulo `rows`
579 fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580 match a - b {
581 n if n < 0 => n + rows,
582 n => n,
583 }
584 }
585 // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587 assert!(term.disp_srow() >= 0);
588 assert!(term.disp_erow() >= 0);
589 assert!(term.hist_srow() >= 0);
590 assert!(term.hist_erow() >= 0);
591 assert!(term.offset() >= 0);
592 assert!(term.disp_srow() <= ring_rows);
593 assert!(term.disp_erow() <= ring_rows);
594 assert!(term.hist_srow() <= ring_rows);
595 assert!(term.hist_erow() <= ring_rows);
596 assert!(term.offset() <= ring_rows);
597
598 assert_eq!(term.ring_srow(), 0);
599 assert_eq!(term.ring_erow(), ring_rows - 1);
600 assert_eq!(
601 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602 term.display_rows()
603 );
604 assert_eq!(
605 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606 term.history_rows()
607 );
608
609 assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610 assert_eq!(term.ring_srow(), 0);
611
612 /// Local function to read back all rows from the display into a long string.
613 /// Does not include scrollback history.
614 /// Trims trailing blanks on each line
615 fn read_disp(term: &Terminal) -> String {
616 let rows = term.display_rows();
617 let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618 for row in 0..rows {
619 let r = term.u8c_disp_row(row).trim();
620 // Iterate through a row, accumulating [u8]
621 for c in r.iter() {
622 // Note: Sometimes utf-8 length is > 1
623 text.extend_from_slice(c.text_utf8());
624 }
625 text.extend_from_slice(b"\n");
626 }
627 // Return the result as a string
628 std::str::from_utf8(&text).unwrap().to_string()
629 }
630
631 term.clear();
632 term.append("Top line ↑ (up-arrow)");
633 term.set_text_attrib(Attrib::Underline);
634 term.append(" ");
635 term.set_text_attrib(Attrib::Normal);
636 term.append(" \n");
637 let mut text_out = read_disp(term);
638 // Trim trailing empty lines
639 text_out = text_out.trim_end_matches(&"\n").to_string();
640 // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642 assert_eq!(text_out, "Top line ↑ (up-arrow) ");
643 let r = term.u8c_disp_row(0);
644 assert_eq!(r.col(0).text_utf8(), b"T");
645 assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646 assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647 let r = term.u8c_disp_row(1);
648 assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649 assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651 // Clear the screen again, then append test text, then read it back and compare
652 let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656 Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659 term.clear_history();
660 term.clear();
661 let bg_save = term.text_bg_color();
662 let fg_save = term.text_fg_color();
663 term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664 term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665 term.append(test_text);
666 term.set_text_bg_color(bg_save);
667 term.set_text_fg_color(fg_save);
668
669 let mut text_out = read_disp(term);
670 // Trim trailing empty lines
671 text_out = text_out.trim_end_matches(&"\n").to_string();
672 assert_eq!(test_text, text_out);
673
674 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676 assert_eq!(term.ring_srow(), 0);
677 assert_eq!(term.ring_erow(), ring_rows - 1);
678 assert_eq!(
679 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680 term.display_rows()
681 );
682 assert_eq!(
683 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684 term.history_rows()
685 );
686
687 term.append(&format!(
688 "\n\nScreen has {} rows of {} columns.\n",
689 term.display_rows(),
690 term.display_columns()
691 ));
692
693 term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
695
696//--------------------------------------------------------------------------------------
697/// Yet another set of tests for misc cursor functions and other stuff
698/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700 term.take_focus().unwrap();
701
702 // Test the attr_fg_color and attr_bg_color methods.
703 // Put a single character 'A' into the buffer and check it
704 term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705 term.set_text_bg_color(Color::TransparentBg);
706 term.set_text_fg_color(Color::XtermWhite);
707 term.append("A");
708 let r = &term.u8c_disp_row(0);
709 let uc = r.col(0);
710 assert_eq!(uc.text_utf8(), b"A");
711 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712 assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713 assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714 assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715 assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717 // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718 term.clear();
719 term.set_text_fg_color_xterm(XtermColor::White);
720 term.set_text_bg_color_xterm(XtermColor::Black);
721 assert_eq!(term.text_attrib(), Attrib::Normal);
722
723 assert!(term.ansi());
724 term.append("B\x1b[32mC\x1b[1mD\n");
725
726 let r = &term.u8c_disp_row(0);
727 let uc = r.col(0);
728 assert_eq!(uc.text_utf8(), b"B");
729 assert!(uc.is_char(b'B'));
730 assert!(!uc.is_char(b'A'));
731 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735 assert_eq!(
736 &uc.charflags(),
737 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738 );
739
740 let uc = r.col(1);
741 assert_eq!(uc.text_utf8(), b"C");
742 assert!(uc.is_char(b'C'));
743 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747 assert_eq!(
748 &uc.charflags(),
749 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750 );
751
752 let uc = r.col(2);
753 assert_eq!(uc.text_utf8(), b"D");
754 assert!(uc.is_char(b'D'));
755 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757 assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759 assert_eq!(
760 &uc.attr_fgcolor(Some(term)),
761 &Color::from_rgb(0x20, 0xf0, 0x20)
762 );
763 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764 assert_eq!(
765 &uc.charflags(),
766 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767 );
768
769 // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770 term.clear();
771 term.set_text_fg_color_xterm(XtermColor::White);
772 term.set_text_bg_color_xterm(XtermColor::Black);
773 term.set_text_attrib(Attrib::Normal);
774 assert_eq!(term.text_attrib(), Attrib::Normal);
775
776 assert!(term.ansi());
777 term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779 let r = &term.u8c_disp_row(0);
780 let uc = r.col(0);
781 assert_eq!(uc.text_utf8(), b"B");
782 assert!(uc.is_char(b'B'));
783 assert!(!uc.is_char(b'A'));
784 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788 assert_eq!(
789 &uc.charflags(),
790 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791 );
792
793 let uc = r.col(1);
794 assert_eq!(uc.text_utf8(), b"C");
795 assert!(uc.is_char(b'C'));
796 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800 assert_eq!(
801 &uc.charflags(),
802 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803 );
804
805 let uc = r.col(2);
806 assert_eq!(uc.text_utf8(), b"D");
807 assert!(uc.is_char(b'D'));
808 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812 assert_eq!(
813 &uc.charflags(),
814 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815 );
816
817 let uc = r.col(3);
818 assert_eq!(uc.text_utf8(), b"E");
819 assert!(uc.is_char(b'E'));
820 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822 assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823 assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824 assert_eq!(
825 &uc.charflags(),
826 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827 );
828
829 // Test some miscellaneous Utf8 constants
830 assert_eq!(uc.length(), 1);
831 assert_eq!(uc.max_utf8(), 4);
832 assert_eq!(uc.pwidth(), 9.0);
833 assert_eq!(uc.pwidth_int(), 9);
834
835 term.set_text_fg_color_xterm(XtermColor::White);
836 term.set_text_bg_color_xterm(XtermColor::Black);
837 term.clear();
838 term.set_text_attrib(Attrib::Normal);
839
840 // Mouse selection functions
841 term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842 term.clear_mouse_selection();
843 assert_eq!(term.get_selection(), None);
844
845 // Play with cursor position
846 term.append("0123456789\n"); // Set up test pattern
847 term.append("ABCDEFGHIJ\n");
848 term.append("abcdefghij\n");
849
850 term.set_cursor_row(1);
851 assert_eq!(term.cursor_row(), 1);
852 term.set_cursor_col(1);
853 assert_eq!(term.cursor_col(), 1);
854 assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856 term.append("----"); // Overwrites text at cursor and moves cursor forward
857 assert_eq!(term.cursor_row(), 1);
858 assert_eq!(term.cursor_col(), 5);
859 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860 term.set_cursor_col(1);
861 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863 term.cursor_up(1, false);
864 assert_eq!(term.cursor_row(), 0);
865 assert_eq!(term.cursor_col(), 1);
866 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868 // Hit top of screen, so nothing happens
869 term.cursor_up(1, false);
870 assert_eq!(term.cursor_row(), 0);
871 assert_eq!(term.cursor_col(), 1);
872 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874 // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875 term.cursor_up(1, true);
876 assert_eq!(term.cursor_row(), 0);
877 assert_eq!(term.cursor_col(), 1);
878 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880 // Go back down to the overwritten text
881 term.cursor_down(2, false);
882 assert_eq!(term.cursor_row(), 2);
883 assert_eq!(term.cursor_col(), 1);
884 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886 // Go right past the overwritten text
887 term.cursor_right(4, false);
888 assert_eq!(term.cursor_row(), 2);
889 assert_eq!(term.cursor_col(), 5);
890 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892 // Go left to the end of the overwritten text
893 term.cursor_left(1);
894 assert_eq!(term.cursor_row(), 2);
895 assert_eq!(term.cursor_col(), 4);
896 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898 // Scroll back down, removing the blank line at the top.
899 // Cursor stays in place, the text moves under it.
900 term.scroll(1);
901 assert_eq!(term.cursor_row(), 2);
902 assert_eq!(term.cursor_col(), 4);
903 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905 // Clear from here to end-of-line
906 term.clear_eol();
907 assert_eq!(term.cursor_row(), 2);
908 assert_eq!(term.cursor_col(), 4);
909 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911 // Now clear from here to start-of-line. Cursor does not move.
912 term.clear_sol();
913 assert_eq!(term.cursor_row(), 2);
914 assert_eq!(term.cursor_col(), 4);
915 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916 term.cursor_left(1);
917 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918 term.set_cursor_col(0);
919 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921 // Clear some lines
922 term.clear_line(1);
923 assert_eq!(term.cursor_row(), 2);
924 assert_eq!(term.cursor_col(), 0);
925 term.set_cursor_row(1);
926 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927 term.set_cursor_row(3);
928 term.clear_cur_line();
929 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930 assert_eq!(term.cursor_row(), 3);
931 assert_eq!(term.cursor_col(), 0);
932
933 term.append("Two lines above are intentionally left blank.\n");
934 assert_eq!(term.cursor_row(), 4);
935 assert_eq!(term.cursor_col(), 0);
936
937 // Set up the test pattern again, then play with insert/delete
938 term.append("0123456789\n");
939 term.append("ABCDEFGHIJ\n");
940 term.append("abcdefghij\n");
941 assert_eq!(term.cursor_row(), 7);
942
943 term.set_cursor_row(4);
944 term.set_cursor_col(4);
945 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947 term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949 term.cursor_right(5, false);
950 assert_eq!(term.cursor_col(), 9);
951 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953 // Insert two blank rows above cursor. Cursor stays put.
954 term.insert_rows(2);
955 assert_eq!(term.cursor_row(), 4);
956 assert_eq!(term.cursor_col(), 9);
957 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958 term.cursor_down(2, false); // Go down to find our text again
959 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961 // Go back to the beginning of the inserted 'x' characters and delete them.
962 term.cursor_left(5);
963 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964 term.delete_cur_chars(5);
965 assert_eq!(term.cursor_row(), 6);
966 assert_eq!(term.cursor_col(), 4);
967 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969 term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970 term.cursor_down(1, false);
971 term.cursor_left(2);
972 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974 term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975 assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976 term.cursor_up(1, false);
977 term.delete_rows(2); // Delete remains of test pattern
978
979 term.set_text_attrib(Attrib::Bold);
980 term.insert_char_eol('-', 3, 15, 20);
981 term.set_cursor_row(3);
982 term.set_cursor_col(15);
983 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984 assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986 term.set_text_attrib(Attrib::Italic);
987 term.append(" and all lines below");
988 term.set_text_attrib(Attrib::Normal);
989 term.cursor_down(1, false);
990
991 let mut hsb = term.hscrollbar();
992 let mut sb = term.scrollbar();
993 hsb.set_value(100.0);
994 assert_eq!(hsb.value(), 100.0);
995 sb.set_value(50.0);
996 assert_eq!(sb.value(), 50.0);
997 hsb.set_value(0.0);
998 assert_eq!(hsb.value(), 0.0);
999 sb.set_value(0.0);
1000 assert_eq!(sb.value(), 0.0);
1001}
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?
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700 term.take_focus().unwrap();
701
702 // Test the attr_fg_color and attr_bg_color methods.
703 // Put a single character 'A' into the buffer and check it
704 term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705 term.set_text_bg_color(Color::TransparentBg);
706 term.set_text_fg_color(Color::XtermWhite);
707 term.append("A");
708 let r = &term.u8c_disp_row(0);
709 let uc = r.col(0);
710 assert_eq!(uc.text_utf8(), b"A");
711 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712 assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713 assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714 assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715 assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717 // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718 term.clear();
719 term.set_text_fg_color_xterm(XtermColor::White);
720 term.set_text_bg_color_xterm(XtermColor::Black);
721 assert_eq!(term.text_attrib(), Attrib::Normal);
722
723 assert!(term.ansi());
724 term.append("B\x1b[32mC\x1b[1mD\n");
725
726 let r = &term.u8c_disp_row(0);
727 let uc = r.col(0);
728 assert_eq!(uc.text_utf8(), b"B");
729 assert!(uc.is_char(b'B'));
730 assert!(!uc.is_char(b'A'));
731 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735 assert_eq!(
736 &uc.charflags(),
737 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738 );
739
740 let uc = r.col(1);
741 assert_eq!(uc.text_utf8(), b"C");
742 assert!(uc.is_char(b'C'));
743 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747 assert_eq!(
748 &uc.charflags(),
749 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750 );
751
752 let uc = r.col(2);
753 assert_eq!(uc.text_utf8(), b"D");
754 assert!(uc.is_char(b'D'));
755 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757 assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759 assert_eq!(
760 &uc.attr_fgcolor(Some(term)),
761 &Color::from_rgb(0x20, 0xf0, 0x20)
762 );
763 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764 assert_eq!(
765 &uc.charflags(),
766 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767 );
768
769 // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770 term.clear();
771 term.set_text_fg_color_xterm(XtermColor::White);
772 term.set_text_bg_color_xterm(XtermColor::Black);
773 term.set_text_attrib(Attrib::Normal);
774 assert_eq!(term.text_attrib(), Attrib::Normal);
775
776 assert!(term.ansi());
777 term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779 let r = &term.u8c_disp_row(0);
780 let uc = r.col(0);
781 assert_eq!(uc.text_utf8(), b"B");
782 assert!(uc.is_char(b'B'));
783 assert!(!uc.is_char(b'A'));
784 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788 assert_eq!(
789 &uc.charflags(),
790 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791 );
792
793 let uc = r.col(1);
794 assert_eq!(uc.text_utf8(), b"C");
795 assert!(uc.is_char(b'C'));
796 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800 assert_eq!(
801 &uc.charflags(),
802 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803 );
804
805 let uc = r.col(2);
806 assert_eq!(uc.text_utf8(), b"D");
807 assert!(uc.is_char(b'D'));
808 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812 assert_eq!(
813 &uc.charflags(),
814 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815 );
816
817 let uc = r.col(3);
818 assert_eq!(uc.text_utf8(), b"E");
819 assert!(uc.is_char(b'E'));
820 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822 assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823 assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824 assert_eq!(
825 &uc.charflags(),
826 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827 );
828
829 // Test some miscellaneous Utf8 constants
830 assert_eq!(uc.length(), 1);
831 assert_eq!(uc.max_utf8(), 4);
832 assert_eq!(uc.pwidth(), 9.0);
833 assert_eq!(uc.pwidth_int(), 9);
834
835 term.set_text_fg_color_xterm(XtermColor::White);
836 term.set_text_bg_color_xterm(XtermColor::Black);
837 term.clear();
838 term.set_text_attrib(Attrib::Normal);
839
840 // Mouse selection functions
841 term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842 term.clear_mouse_selection();
843 assert_eq!(term.get_selection(), None);
844
845 // Play with cursor position
846 term.append("0123456789\n"); // Set up test pattern
847 term.append("ABCDEFGHIJ\n");
848 term.append("abcdefghij\n");
849
850 term.set_cursor_row(1);
851 assert_eq!(term.cursor_row(), 1);
852 term.set_cursor_col(1);
853 assert_eq!(term.cursor_col(), 1);
854 assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856 term.append("----"); // Overwrites text at cursor and moves cursor forward
857 assert_eq!(term.cursor_row(), 1);
858 assert_eq!(term.cursor_col(), 5);
859 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860 term.set_cursor_col(1);
861 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863 term.cursor_up(1, false);
864 assert_eq!(term.cursor_row(), 0);
865 assert_eq!(term.cursor_col(), 1);
866 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868 // Hit top of screen, so nothing happens
869 term.cursor_up(1, false);
870 assert_eq!(term.cursor_row(), 0);
871 assert_eq!(term.cursor_col(), 1);
872 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874 // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875 term.cursor_up(1, true);
876 assert_eq!(term.cursor_row(), 0);
877 assert_eq!(term.cursor_col(), 1);
878 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880 // Go back down to the overwritten text
881 term.cursor_down(2, false);
882 assert_eq!(term.cursor_row(), 2);
883 assert_eq!(term.cursor_col(), 1);
884 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886 // Go right past the overwritten text
887 term.cursor_right(4, false);
888 assert_eq!(term.cursor_row(), 2);
889 assert_eq!(term.cursor_col(), 5);
890 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892 // Go left to the end of the overwritten text
893 term.cursor_left(1);
894 assert_eq!(term.cursor_row(), 2);
895 assert_eq!(term.cursor_col(), 4);
896 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898 // Scroll back down, removing the blank line at the top.
899 // Cursor stays in place, the text moves under it.
900 term.scroll(1);
901 assert_eq!(term.cursor_row(), 2);
902 assert_eq!(term.cursor_col(), 4);
903 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905 // Clear from here to end-of-line
906 term.clear_eol();
907 assert_eq!(term.cursor_row(), 2);
908 assert_eq!(term.cursor_col(), 4);
909 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911 // Now clear from here to start-of-line. Cursor does not move.
912 term.clear_sol();
913 assert_eq!(term.cursor_row(), 2);
914 assert_eq!(term.cursor_col(), 4);
915 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916 term.cursor_left(1);
917 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918 term.set_cursor_col(0);
919 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921 // Clear some lines
922 term.clear_line(1);
923 assert_eq!(term.cursor_row(), 2);
924 assert_eq!(term.cursor_col(), 0);
925 term.set_cursor_row(1);
926 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927 term.set_cursor_row(3);
928 term.clear_cur_line();
929 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930 assert_eq!(term.cursor_row(), 3);
931 assert_eq!(term.cursor_col(), 0);
932
933 term.append("Two lines above are intentionally left blank.\n");
934 assert_eq!(term.cursor_row(), 4);
935 assert_eq!(term.cursor_col(), 0);
936
937 // Set up the test pattern again, then play with insert/delete
938 term.append("0123456789\n");
939 term.append("ABCDEFGHIJ\n");
940 term.append("abcdefghij\n");
941 assert_eq!(term.cursor_row(), 7);
942
943 term.set_cursor_row(4);
944 term.set_cursor_col(4);
945 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947 term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949 term.cursor_right(5, false);
950 assert_eq!(term.cursor_col(), 9);
951 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953 // Insert two blank rows above cursor. Cursor stays put.
954 term.insert_rows(2);
955 assert_eq!(term.cursor_row(), 4);
956 assert_eq!(term.cursor_col(), 9);
957 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958 term.cursor_down(2, false); // Go down to find our text again
959 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961 // Go back to the beginning of the inserted 'x' characters and delete them.
962 term.cursor_left(5);
963 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964 term.delete_cur_chars(5);
965 assert_eq!(term.cursor_row(), 6);
966 assert_eq!(term.cursor_col(), 4);
967 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969 term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970 term.cursor_down(1, false);
971 term.cursor_left(2);
972 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974 term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975 assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976 term.cursor_up(1, false);
977 term.delete_rows(2); // Delete remains of test pattern
978
979 term.set_text_attrib(Attrib::Bold);
980 term.insert_char_eol('-', 3, 15, 20);
981 term.set_cursor_row(3);
982 term.set_cursor_col(15);
983 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984 assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986 term.set_text_attrib(Attrib::Italic);
987 term.append(" and all lines below");
988 term.set_text_attrib(Attrib::Normal);
989 term.cursor_down(1, false);
990
991 let mut hsb = term.hscrollbar();
992 let mut sb = term.scrollbar();
993 hsb.set_value(100.0);
994 assert_eq!(hsb.value(), 100.0);
995 sb.set_value(50.0);
996 assert_eq!(sb.value(), 50.0);
997 hsb.set_value(0.0);
998 assert_eq!(hsb.value(), 0.0);
999 sb.set_value(0.0);
1000 assert_eq!(sb.value(), 0.0);
1001}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
425//--------------------------------------------------------------------------------------
426/// More tests that run when the menu bar Test1 is clicked
427fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
428 term.take_focus().unwrap();
429 term.reset_terminal();
430 term.append("0123456789 0\n");
431 term.append("0123456789 1\n");
432 term.append("0123456789 2\n");
433 term.append("0123456789 3\n");
434 term.append("0123456789 4\n");
435 term.append("0123456789 5\n");
436 term.append("0123456789 6\n");
437 term.append("0123456789 7\n");
438 term.append("0123456789 8\n");
439 term.append("0123456789 9\n");
440 term.append("------------\n");
441
442 term.set_text_fg_color(Color::Green);
443 term.plot_char('A', 0, 0);
444 term.plot_char('B', 1, 1);
445 term.plot_char('C', 2, 2);
446 term.plot_char('D', 3, 3);
447 term.plot_char('E', 4, 4);
448 term.plot_char('F', 5, 5);
449 term.set_text_fg_color(Color::XtermWhite);
450
451 assert_eq!(term.cursor_row(), 11);
452 assert_eq!(term.cursor_col(), 0);
453
454 term.set_text_bg_color(Color::DarkBlue);
455 term.plot_char_utf8('b', 8, 1);
456 term.plot_char_utf8('↑', 9, 1);
457 term.plot_char_utf8('c', 8, 2);
458 term.plot_char_utf8('↑', 9, 2);
459 term.plot_char_utf8('d', 8, 3);
460 term.plot_char_utf8('↑', 9, 3);
461 term.plot_char_utf8('e', 8, 4);
462 term.plot_char_utf8('↑', 9, 4);
463 term.plot_char_utf8('f', 8, 5);
464 term.plot_char_utf8('↑', 9, 5);
465 term.plot_char_utf8('g', 8, 6);
466 term.plot_char_utf8('↑', 9, 6);
467 term.set_text_bg_color(Color::TransparentBg);
468
469 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
470 term.append("Done!\n");
471 term.set_text_attrib(Attrib::Normal);
472}
473
474//--------------------------------------------------------------------------------------
475/// More tests that run when the menu bar button Test2 is clicked
476fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
477 term.take_focus().unwrap();
478 term.reset_terminal();
479
480 for i in 0..50 {
481 term.append(&format!("{i}\n"));
482 }
483 assert_eq!(term.history_rows(), 100);
484
485 term.clear_history();
486 assert_eq!(term.history_use(), 0);
487
488 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
489 term.append("\nDone!\n");
490 term.set_text_attrib(Attrib::Normal);
491}
492
493//--------------------------------------------------------------------------------------
494/// Another set of tests that run when Test3 is clicked
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496 term.take_focus().unwrap();
497 term.reset_terminal();
498 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500 assert_eq!(term.history_use(), 0);
501 term.clear();
502 assert_eq!(term.cursor_row(), 0);
503 assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505 term.append("Test\ntext\na\nb\nc\nd");
506 assert_eq!(term.cursor_row(), 5);
507 let hist = term.history_use();
508 term.clear_screen_home(false);
509 assert_eq!(term.cursor_row(), 0);
510 assert_eq!(term.history_use(), hist); // History not changed
511
512 term.append("Test\ntext\na\nb\nc\nd\ne");
513 assert_eq!(term.cursor_row(), 6);
514 term.clear_screen_home(true);
515 assert_eq!(term.cursor_row(), 0);
516
517 term.append("Test\ntext\na\nb\nc\n");
518 assert_eq!(term.cursor_row(), 5);
519 term.clear_to_color(Color::DarkBlue);
520 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521 assert_eq!(term.text_bg_color(), Color::TransparentBg);
522 assert_eq!(term.cursor_row(), 0);
523
524 // Test cursor_home()
525 term.append("Test\n\n\n\n\n\n\n\n\n\n");
526 assert_eq!(term.cursor_row(), 10);
527 term.cursor_home();
528 assert_eq!(term.cursor_row(), 0);
529
530 // Test the widget color
531 assert_eq!(term.color(), Color::Black); // Default
532 term.set_color(Color::DarkGreen);
533 assert_eq!(term.color(), Color::DarkGreen);
534 term.set_color(Color::Black);
535 assert_eq!(term.color(), Color::Black);
536 term.append(
537 "This should be one line of white text on black, embedded into the top of a blue field.\n",
538 );
539
540 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541 term.set_output_translate(OutFlags::OFF);
542 assert_eq!(term.output_translate(), OutFlags::OFF);
543 term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547 term.append("\nDone!\n");
548 term.set_text_attrib(Attrib::Normal);
549}
550
551//--------------------------------------------------------------------------------------
552/// Another set of tests for the ring-buffer access methods
553/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555 let sel_len = term.selection_text_len();
556 let sel = term.selection_text();
557
558 term.take_focus().unwrap();
559 term.reset_terminal();
560 // Test the Utf8Char primitive
561 let uc = Utf8Char::new(b'Q');
562 let uc1 = uc.text_utf8();
563 assert_eq!(&uc1, b"Q");
564 assert_eq!(&uc.attrib(), &Attrib::Normal);
565 assert_eq!(
566 &uc.charflags(),
567 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568 );
569 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570 assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572 let ring_rows = term.ring_rows();
573
574 term.take_focus().unwrap();
575 term.clear_history();
576 assert_eq!(term.history_use(), 0);
577
578 // Subtract row numbers, modulo `rows`
579 fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580 match a - b {
581 n if n < 0 => n + rows,
582 n => n,
583 }
584 }
585 // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587 assert!(term.disp_srow() >= 0);
588 assert!(term.disp_erow() >= 0);
589 assert!(term.hist_srow() >= 0);
590 assert!(term.hist_erow() >= 0);
591 assert!(term.offset() >= 0);
592 assert!(term.disp_srow() <= ring_rows);
593 assert!(term.disp_erow() <= ring_rows);
594 assert!(term.hist_srow() <= ring_rows);
595 assert!(term.hist_erow() <= ring_rows);
596 assert!(term.offset() <= ring_rows);
597
598 assert_eq!(term.ring_srow(), 0);
599 assert_eq!(term.ring_erow(), ring_rows - 1);
600 assert_eq!(
601 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602 term.display_rows()
603 );
604 assert_eq!(
605 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606 term.history_rows()
607 );
608
609 assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610 assert_eq!(term.ring_srow(), 0);
611
612 /// Local function to read back all rows from the display into a long string.
613 /// Does not include scrollback history.
614 /// Trims trailing blanks on each line
615 fn read_disp(term: &Terminal) -> String {
616 let rows = term.display_rows();
617 let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618 for row in 0..rows {
619 let r = term.u8c_disp_row(row).trim();
620 // Iterate through a row, accumulating [u8]
621 for c in r.iter() {
622 // Note: Sometimes utf-8 length is > 1
623 text.extend_from_slice(c.text_utf8());
624 }
625 text.extend_from_slice(b"\n");
626 }
627 // Return the result as a string
628 std::str::from_utf8(&text).unwrap().to_string()
629 }
630
631 term.clear();
632 term.append("Top line ↑ (up-arrow)");
633 term.set_text_attrib(Attrib::Underline);
634 term.append(" ");
635 term.set_text_attrib(Attrib::Normal);
636 term.append(" \n");
637 let mut text_out = read_disp(term);
638 // Trim trailing empty lines
639 text_out = text_out.trim_end_matches(&"\n").to_string();
640 // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642 assert_eq!(text_out, "Top line ↑ (up-arrow) ");
643 let r = term.u8c_disp_row(0);
644 assert_eq!(r.col(0).text_utf8(), b"T");
645 assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646 assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647 let r = term.u8c_disp_row(1);
648 assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649 assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651 // Clear the screen again, then append test text, then read it back and compare
652 let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656 Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659 term.clear_history();
660 term.clear();
661 let bg_save = term.text_bg_color();
662 let fg_save = term.text_fg_color();
663 term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664 term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665 term.append(test_text);
666 term.set_text_bg_color(bg_save);
667 term.set_text_fg_color(fg_save);
668
669 let mut text_out = read_disp(term);
670 // Trim trailing empty lines
671 text_out = text_out.trim_end_matches(&"\n").to_string();
672 assert_eq!(test_text, text_out);
673
674 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676 assert_eq!(term.ring_srow(), 0);
677 assert_eq!(term.ring_erow(), ring_rows - 1);
678 assert_eq!(
679 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680 term.display_rows()
681 );
682 assert_eq!(
683 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684 term.history_rows()
685 );
686
687 term.append(&format!(
688 "\n\nScreen has {} rows of {} columns.\n",
689 term.display_rows(),
690 term.display_columns()
691 ));
692
693 term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
695
696//--------------------------------------------------------------------------------------
697/// Yet another set of tests for misc cursor functions and other stuff
698/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700 term.take_focus().unwrap();
701
702 // Test the attr_fg_color and attr_bg_color methods.
703 // Put a single character 'A' into the buffer and check it
704 term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705 term.set_text_bg_color(Color::TransparentBg);
706 term.set_text_fg_color(Color::XtermWhite);
707 term.append("A");
708 let r = &term.u8c_disp_row(0);
709 let uc = r.col(0);
710 assert_eq!(uc.text_utf8(), b"A");
711 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712 assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713 assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714 assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715 assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717 // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718 term.clear();
719 term.set_text_fg_color_xterm(XtermColor::White);
720 term.set_text_bg_color_xterm(XtermColor::Black);
721 assert_eq!(term.text_attrib(), Attrib::Normal);
722
723 assert!(term.ansi());
724 term.append("B\x1b[32mC\x1b[1mD\n");
725
726 let r = &term.u8c_disp_row(0);
727 let uc = r.col(0);
728 assert_eq!(uc.text_utf8(), b"B");
729 assert!(uc.is_char(b'B'));
730 assert!(!uc.is_char(b'A'));
731 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735 assert_eq!(
736 &uc.charflags(),
737 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738 );
739
740 let uc = r.col(1);
741 assert_eq!(uc.text_utf8(), b"C");
742 assert!(uc.is_char(b'C'));
743 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747 assert_eq!(
748 &uc.charflags(),
749 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750 );
751
752 let uc = r.col(2);
753 assert_eq!(uc.text_utf8(), b"D");
754 assert!(uc.is_char(b'D'));
755 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757 assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759 assert_eq!(
760 &uc.attr_fgcolor(Some(term)),
761 &Color::from_rgb(0x20, 0xf0, 0x20)
762 );
763 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764 assert_eq!(
765 &uc.charflags(),
766 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767 );
768
769 // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770 term.clear();
771 term.set_text_fg_color_xterm(XtermColor::White);
772 term.set_text_bg_color_xterm(XtermColor::Black);
773 term.set_text_attrib(Attrib::Normal);
774 assert_eq!(term.text_attrib(), Attrib::Normal);
775
776 assert!(term.ansi());
777 term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779 let r = &term.u8c_disp_row(0);
780 let uc = r.col(0);
781 assert_eq!(uc.text_utf8(), b"B");
782 assert!(uc.is_char(b'B'));
783 assert!(!uc.is_char(b'A'));
784 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788 assert_eq!(
789 &uc.charflags(),
790 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791 );
792
793 let uc = r.col(1);
794 assert_eq!(uc.text_utf8(), b"C");
795 assert!(uc.is_char(b'C'));
796 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800 assert_eq!(
801 &uc.charflags(),
802 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803 );
804
805 let uc = r.col(2);
806 assert_eq!(uc.text_utf8(), b"D");
807 assert!(uc.is_char(b'D'));
808 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812 assert_eq!(
813 &uc.charflags(),
814 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815 );
816
817 let uc = r.col(3);
818 assert_eq!(uc.text_utf8(), b"E");
819 assert!(uc.is_char(b'E'));
820 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822 assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823 assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824 assert_eq!(
825 &uc.charflags(),
826 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827 );
828
829 // Test some miscellaneous Utf8 constants
830 assert_eq!(uc.length(), 1);
831 assert_eq!(uc.max_utf8(), 4);
832 assert_eq!(uc.pwidth(), 9.0);
833 assert_eq!(uc.pwidth_int(), 9);
834
835 term.set_text_fg_color_xterm(XtermColor::White);
836 term.set_text_bg_color_xterm(XtermColor::Black);
837 term.clear();
838 term.set_text_attrib(Attrib::Normal);
839
840 // Mouse selection functions
841 term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842 term.clear_mouse_selection();
843 assert_eq!(term.get_selection(), None);
844
845 // Play with cursor position
846 term.append("0123456789\n"); // Set up test pattern
847 term.append("ABCDEFGHIJ\n");
848 term.append("abcdefghij\n");
849
850 term.set_cursor_row(1);
851 assert_eq!(term.cursor_row(), 1);
852 term.set_cursor_col(1);
853 assert_eq!(term.cursor_col(), 1);
854 assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856 term.append("----"); // Overwrites text at cursor and moves cursor forward
857 assert_eq!(term.cursor_row(), 1);
858 assert_eq!(term.cursor_col(), 5);
859 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860 term.set_cursor_col(1);
861 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863 term.cursor_up(1, false);
864 assert_eq!(term.cursor_row(), 0);
865 assert_eq!(term.cursor_col(), 1);
866 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868 // Hit top of screen, so nothing happens
869 term.cursor_up(1, false);
870 assert_eq!(term.cursor_row(), 0);
871 assert_eq!(term.cursor_col(), 1);
872 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874 // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875 term.cursor_up(1, true);
876 assert_eq!(term.cursor_row(), 0);
877 assert_eq!(term.cursor_col(), 1);
878 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880 // Go back down to the overwritten text
881 term.cursor_down(2, false);
882 assert_eq!(term.cursor_row(), 2);
883 assert_eq!(term.cursor_col(), 1);
884 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886 // Go right past the overwritten text
887 term.cursor_right(4, false);
888 assert_eq!(term.cursor_row(), 2);
889 assert_eq!(term.cursor_col(), 5);
890 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892 // Go left to the end of the overwritten text
893 term.cursor_left(1);
894 assert_eq!(term.cursor_row(), 2);
895 assert_eq!(term.cursor_col(), 4);
896 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898 // Scroll back down, removing the blank line at the top.
899 // Cursor stays in place, the text moves under it.
900 term.scroll(1);
901 assert_eq!(term.cursor_row(), 2);
902 assert_eq!(term.cursor_col(), 4);
903 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905 // Clear from here to end-of-line
906 term.clear_eol();
907 assert_eq!(term.cursor_row(), 2);
908 assert_eq!(term.cursor_col(), 4);
909 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911 // Now clear from here to start-of-line. Cursor does not move.
912 term.clear_sol();
913 assert_eq!(term.cursor_row(), 2);
914 assert_eq!(term.cursor_col(), 4);
915 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916 term.cursor_left(1);
917 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918 term.set_cursor_col(0);
919 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921 // Clear some lines
922 term.clear_line(1);
923 assert_eq!(term.cursor_row(), 2);
924 assert_eq!(term.cursor_col(), 0);
925 term.set_cursor_row(1);
926 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927 term.set_cursor_row(3);
928 term.clear_cur_line();
929 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930 assert_eq!(term.cursor_row(), 3);
931 assert_eq!(term.cursor_col(), 0);
932
933 term.append("Two lines above are intentionally left blank.\n");
934 assert_eq!(term.cursor_row(), 4);
935 assert_eq!(term.cursor_col(), 0);
936
937 // Set up the test pattern again, then play with insert/delete
938 term.append("0123456789\n");
939 term.append("ABCDEFGHIJ\n");
940 term.append("abcdefghij\n");
941 assert_eq!(term.cursor_row(), 7);
942
943 term.set_cursor_row(4);
944 term.set_cursor_col(4);
945 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947 term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949 term.cursor_right(5, false);
950 assert_eq!(term.cursor_col(), 9);
951 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953 // Insert two blank rows above cursor. Cursor stays put.
954 term.insert_rows(2);
955 assert_eq!(term.cursor_row(), 4);
956 assert_eq!(term.cursor_col(), 9);
957 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958 term.cursor_down(2, false); // Go down to find our text again
959 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961 // Go back to the beginning of the inserted 'x' characters and delete them.
962 term.cursor_left(5);
963 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964 term.delete_cur_chars(5);
965 assert_eq!(term.cursor_row(), 6);
966 assert_eq!(term.cursor_col(), 4);
967 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969 term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970 term.cursor_down(1, false);
971 term.cursor_left(2);
972 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974 term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975 assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976 term.cursor_up(1, false);
977 term.delete_rows(2); // Delete remains of test pattern
978
979 term.set_text_attrib(Attrib::Bold);
980 term.insert_char_eol('-', 3, 15, 20);
981 term.set_cursor_row(3);
982 term.set_cursor_col(15);
983 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984 assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986 term.set_text_attrib(Attrib::Italic);
987 term.append(" and all lines below");
988 term.set_text_attrib(Attrib::Normal);
989 term.cursor_down(1, false);
990
991 let mut hsb = term.hscrollbar();
992 let mut sb = term.scrollbar();
993 hsb.set_value(100.0);
994 assert_eq!(hsb.value(), 100.0);
995 sb.set_value(50.0);
996 assert_eq!(sb.value(), 50.0);
997 hsb.set_value(0.0);
998 assert_eq!(hsb.value(), 0.0);
999 sb.set_value(0.0);
1000 assert_eq!(sb.value(), 0.0);
1001}
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?
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700 term.take_focus().unwrap();
701
702 // Test the attr_fg_color and attr_bg_color methods.
703 // Put a single character 'A' into the buffer and check it
704 term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705 term.set_text_bg_color(Color::TransparentBg);
706 term.set_text_fg_color(Color::XtermWhite);
707 term.append("A");
708 let r = &term.u8c_disp_row(0);
709 let uc = r.col(0);
710 assert_eq!(uc.text_utf8(), b"A");
711 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712 assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713 assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714 assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715 assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717 // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718 term.clear();
719 term.set_text_fg_color_xterm(XtermColor::White);
720 term.set_text_bg_color_xterm(XtermColor::Black);
721 assert_eq!(term.text_attrib(), Attrib::Normal);
722
723 assert!(term.ansi());
724 term.append("B\x1b[32mC\x1b[1mD\n");
725
726 let r = &term.u8c_disp_row(0);
727 let uc = r.col(0);
728 assert_eq!(uc.text_utf8(), b"B");
729 assert!(uc.is_char(b'B'));
730 assert!(!uc.is_char(b'A'));
731 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735 assert_eq!(
736 &uc.charflags(),
737 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738 );
739
740 let uc = r.col(1);
741 assert_eq!(uc.text_utf8(), b"C");
742 assert!(uc.is_char(b'C'));
743 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747 assert_eq!(
748 &uc.charflags(),
749 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750 );
751
752 let uc = r.col(2);
753 assert_eq!(uc.text_utf8(), b"D");
754 assert!(uc.is_char(b'D'));
755 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757 assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759 assert_eq!(
760 &uc.attr_fgcolor(Some(term)),
761 &Color::from_rgb(0x20, 0xf0, 0x20)
762 );
763 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764 assert_eq!(
765 &uc.charflags(),
766 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767 );
768
769 // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770 term.clear();
771 term.set_text_fg_color_xterm(XtermColor::White);
772 term.set_text_bg_color_xterm(XtermColor::Black);
773 term.set_text_attrib(Attrib::Normal);
774 assert_eq!(term.text_attrib(), Attrib::Normal);
775
776 assert!(term.ansi());
777 term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779 let r = &term.u8c_disp_row(0);
780 let uc = r.col(0);
781 assert_eq!(uc.text_utf8(), b"B");
782 assert!(uc.is_char(b'B'));
783 assert!(!uc.is_char(b'A'));
784 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788 assert_eq!(
789 &uc.charflags(),
790 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791 );
792
793 let uc = r.col(1);
794 assert_eq!(uc.text_utf8(), b"C");
795 assert!(uc.is_char(b'C'));
796 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800 assert_eq!(
801 &uc.charflags(),
802 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803 );
804
805 let uc = r.col(2);
806 assert_eq!(uc.text_utf8(), b"D");
807 assert!(uc.is_char(b'D'));
808 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812 assert_eq!(
813 &uc.charflags(),
814 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815 );
816
817 let uc = r.col(3);
818 assert_eq!(uc.text_utf8(), b"E");
819 assert!(uc.is_char(b'E'));
820 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822 assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823 assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824 assert_eq!(
825 &uc.charflags(),
826 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827 );
828
829 // Test some miscellaneous Utf8 constants
830 assert_eq!(uc.length(), 1);
831 assert_eq!(uc.max_utf8(), 4);
832 assert_eq!(uc.pwidth(), 9.0);
833 assert_eq!(uc.pwidth_int(), 9);
834
835 term.set_text_fg_color_xterm(XtermColor::White);
836 term.set_text_bg_color_xterm(XtermColor::Black);
837 term.clear();
838 term.set_text_attrib(Attrib::Normal);
839
840 // Mouse selection functions
841 term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842 term.clear_mouse_selection();
843 assert_eq!(term.get_selection(), None);
844
845 // Play with cursor position
846 term.append("0123456789\n"); // Set up test pattern
847 term.append("ABCDEFGHIJ\n");
848 term.append("abcdefghij\n");
849
850 term.set_cursor_row(1);
851 assert_eq!(term.cursor_row(), 1);
852 term.set_cursor_col(1);
853 assert_eq!(term.cursor_col(), 1);
854 assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856 term.append("----"); // Overwrites text at cursor and moves cursor forward
857 assert_eq!(term.cursor_row(), 1);
858 assert_eq!(term.cursor_col(), 5);
859 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860 term.set_cursor_col(1);
861 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863 term.cursor_up(1, false);
864 assert_eq!(term.cursor_row(), 0);
865 assert_eq!(term.cursor_col(), 1);
866 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868 // Hit top of screen, so nothing happens
869 term.cursor_up(1, false);
870 assert_eq!(term.cursor_row(), 0);
871 assert_eq!(term.cursor_col(), 1);
872 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874 // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875 term.cursor_up(1, true);
876 assert_eq!(term.cursor_row(), 0);
877 assert_eq!(term.cursor_col(), 1);
878 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880 // Go back down to the overwritten text
881 term.cursor_down(2, false);
882 assert_eq!(term.cursor_row(), 2);
883 assert_eq!(term.cursor_col(), 1);
884 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886 // Go right past the overwritten text
887 term.cursor_right(4, false);
888 assert_eq!(term.cursor_row(), 2);
889 assert_eq!(term.cursor_col(), 5);
890 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892 // Go left to the end of the overwritten text
893 term.cursor_left(1);
894 assert_eq!(term.cursor_row(), 2);
895 assert_eq!(term.cursor_col(), 4);
896 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898 // Scroll back down, removing the blank line at the top.
899 // Cursor stays in place, the text moves under it.
900 term.scroll(1);
901 assert_eq!(term.cursor_row(), 2);
902 assert_eq!(term.cursor_col(), 4);
903 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905 // Clear from here to end-of-line
906 term.clear_eol();
907 assert_eq!(term.cursor_row(), 2);
908 assert_eq!(term.cursor_col(), 4);
909 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911 // Now clear from here to start-of-line. Cursor does not move.
912 term.clear_sol();
913 assert_eq!(term.cursor_row(), 2);
914 assert_eq!(term.cursor_col(), 4);
915 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916 term.cursor_left(1);
917 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918 term.set_cursor_col(0);
919 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921 // Clear some lines
922 term.clear_line(1);
923 assert_eq!(term.cursor_row(), 2);
924 assert_eq!(term.cursor_col(), 0);
925 term.set_cursor_row(1);
926 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927 term.set_cursor_row(3);
928 term.clear_cur_line();
929 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930 assert_eq!(term.cursor_row(), 3);
931 assert_eq!(term.cursor_col(), 0);
932
933 term.append("Two lines above are intentionally left blank.\n");
934 assert_eq!(term.cursor_row(), 4);
935 assert_eq!(term.cursor_col(), 0);
936
937 // Set up the test pattern again, then play with insert/delete
938 term.append("0123456789\n");
939 term.append("ABCDEFGHIJ\n");
940 term.append("abcdefghij\n");
941 assert_eq!(term.cursor_row(), 7);
942
943 term.set_cursor_row(4);
944 term.set_cursor_col(4);
945 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947 term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949 term.cursor_right(5, false);
950 assert_eq!(term.cursor_col(), 9);
951 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953 // Insert two blank rows above cursor. Cursor stays put.
954 term.insert_rows(2);
955 assert_eq!(term.cursor_row(), 4);
956 assert_eq!(term.cursor_col(), 9);
957 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958 term.cursor_down(2, false); // Go down to find our text again
959 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961 // Go back to the beginning of the inserted 'x' characters and delete them.
962 term.cursor_left(5);
963 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964 term.delete_cur_chars(5);
965 assert_eq!(term.cursor_row(), 6);
966 assert_eq!(term.cursor_col(), 4);
967 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969 term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970 term.cursor_down(1, false);
971 term.cursor_left(2);
972 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974 term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975 assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976 term.cursor_up(1, false);
977 term.delete_rows(2); // Delete remains of test pattern
978
979 term.set_text_attrib(Attrib::Bold);
980 term.insert_char_eol('-', 3, 15, 20);
981 term.set_cursor_row(3);
982 term.set_cursor_col(15);
983 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984 assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986 term.set_text_attrib(Attrib::Italic);
987 term.append(" and all lines below");
988 term.set_text_attrib(Attrib::Normal);
989 term.cursor_down(1, false);
990
991 let mut hsb = term.hscrollbar();
992 let mut sb = term.scrollbar();
993 hsb.set_value(100.0);
994 assert_eq!(hsb.value(), 100.0);
995 sb.set_value(50.0);
996 assert_eq!(sb.value(), 50.0);
997 hsb.set_value(0.0);
998 assert_eq!(hsb.value(), 0.0);
999 sb.set_value(0.0);
1000 assert_eq!(sb.value(), 0.0);
1001}
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?
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700 term.take_focus().unwrap();
701
702 // Test the attr_fg_color and attr_bg_color methods.
703 // Put a single character 'A' into the buffer and check it
704 term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705 term.set_text_bg_color(Color::TransparentBg);
706 term.set_text_fg_color(Color::XtermWhite);
707 term.append("A");
708 let r = &term.u8c_disp_row(0);
709 let uc = r.col(0);
710 assert_eq!(uc.text_utf8(), b"A");
711 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712 assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713 assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714 assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715 assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717 // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718 term.clear();
719 term.set_text_fg_color_xterm(XtermColor::White);
720 term.set_text_bg_color_xterm(XtermColor::Black);
721 assert_eq!(term.text_attrib(), Attrib::Normal);
722
723 assert!(term.ansi());
724 term.append("B\x1b[32mC\x1b[1mD\n");
725
726 let r = &term.u8c_disp_row(0);
727 let uc = r.col(0);
728 assert_eq!(uc.text_utf8(), b"B");
729 assert!(uc.is_char(b'B'));
730 assert!(!uc.is_char(b'A'));
731 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735 assert_eq!(
736 &uc.charflags(),
737 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738 );
739
740 let uc = r.col(1);
741 assert_eq!(uc.text_utf8(), b"C");
742 assert!(uc.is_char(b'C'));
743 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747 assert_eq!(
748 &uc.charflags(),
749 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750 );
751
752 let uc = r.col(2);
753 assert_eq!(uc.text_utf8(), b"D");
754 assert!(uc.is_char(b'D'));
755 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757 assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759 assert_eq!(
760 &uc.attr_fgcolor(Some(term)),
761 &Color::from_rgb(0x20, 0xf0, 0x20)
762 );
763 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764 assert_eq!(
765 &uc.charflags(),
766 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767 );
768
769 // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770 term.clear();
771 term.set_text_fg_color_xterm(XtermColor::White);
772 term.set_text_bg_color_xterm(XtermColor::Black);
773 term.set_text_attrib(Attrib::Normal);
774 assert_eq!(term.text_attrib(), Attrib::Normal);
775
776 assert!(term.ansi());
777 term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779 let r = &term.u8c_disp_row(0);
780 let uc = r.col(0);
781 assert_eq!(uc.text_utf8(), b"B");
782 assert!(uc.is_char(b'B'));
783 assert!(!uc.is_char(b'A'));
784 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788 assert_eq!(
789 &uc.charflags(),
790 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791 );
792
793 let uc = r.col(1);
794 assert_eq!(uc.text_utf8(), b"C");
795 assert!(uc.is_char(b'C'));
796 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800 assert_eq!(
801 &uc.charflags(),
802 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803 );
804
805 let uc = r.col(2);
806 assert_eq!(uc.text_utf8(), b"D");
807 assert!(uc.is_char(b'D'));
808 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812 assert_eq!(
813 &uc.charflags(),
814 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815 );
816
817 let uc = r.col(3);
818 assert_eq!(uc.text_utf8(), b"E");
819 assert!(uc.is_char(b'E'));
820 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822 assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823 assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824 assert_eq!(
825 &uc.charflags(),
826 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827 );
828
829 // Test some miscellaneous Utf8 constants
830 assert_eq!(uc.length(), 1);
831 assert_eq!(uc.max_utf8(), 4);
832 assert_eq!(uc.pwidth(), 9.0);
833 assert_eq!(uc.pwidth_int(), 9);
834
835 term.set_text_fg_color_xterm(XtermColor::White);
836 term.set_text_bg_color_xterm(XtermColor::Black);
837 term.clear();
838 term.set_text_attrib(Attrib::Normal);
839
840 // Mouse selection functions
841 term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842 term.clear_mouse_selection();
843 assert_eq!(term.get_selection(), None);
844
845 // Play with cursor position
846 term.append("0123456789\n"); // Set up test pattern
847 term.append("ABCDEFGHIJ\n");
848 term.append("abcdefghij\n");
849
850 term.set_cursor_row(1);
851 assert_eq!(term.cursor_row(), 1);
852 term.set_cursor_col(1);
853 assert_eq!(term.cursor_col(), 1);
854 assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856 term.append("----"); // Overwrites text at cursor and moves cursor forward
857 assert_eq!(term.cursor_row(), 1);
858 assert_eq!(term.cursor_col(), 5);
859 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860 term.set_cursor_col(1);
861 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863 term.cursor_up(1, false);
864 assert_eq!(term.cursor_row(), 0);
865 assert_eq!(term.cursor_col(), 1);
866 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868 // Hit top of screen, so nothing happens
869 term.cursor_up(1, false);
870 assert_eq!(term.cursor_row(), 0);
871 assert_eq!(term.cursor_col(), 1);
872 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874 // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875 term.cursor_up(1, true);
876 assert_eq!(term.cursor_row(), 0);
877 assert_eq!(term.cursor_col(), 1);
878 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880 // Go back down to the overwritten text
881 term.cursor_down(2, false);
882 assert_eq!(term.cursor_row(), 2);
883 assert_eq!(term.cursor_col(), 1);
884 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886 // Go right past the overwritten text
887 term.cursor_right(4, false);
888 assert_eq!(term.cursor_row(), 2);
889 assert_eq!(term.cursor_col(), 5);
890 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892 // Go left to the end of the overwritten text
893 term.cursor_left(1);
894 assert_eq!(term.cursor_row(), 2);
895 assert_eq!(term.cursor_col(), 4);
896 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898 // Scroll back down, removing the blank line at the top.
899 // Cursor stays in place, the text moves under it.
900 term.scroll(1);
901 assert_eq!(term.cursor_row(), 2);
902 assert_eq!(term.cursor_col(), 4);
903 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905 // Clear from here to end-of-line
906 term.clear_eol();
907 assert_eq!(term.cursor_row(), 2);
908 assert_eq!(term.cursor_col(), 4);
909 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911 // Now clear from here to start-of-line. Cursor does not move.
912 term.clear_sol();
913 assert_eq!(term.cursor_row(), 2);
914 assert_eq!(term.cursor_col(), 4);
915 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916 term.cursor_left(1);
917 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918 term.set_cursor_col(0);
919 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921 // Clear some lines
922 term.clear_line(1);
923 assert_eq!(term.cursor_row(), 2);
924 assert_eq!(term.cursor_col(), 0);
925 term.set_cursor_row(1);
926 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927 term.set_cursor_row(3);
928 term.clear_cur_line();
929 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930 assert_eq!(term.cursor_row(), 3);
931 assert_eq!(term.cursor_col(), 0);
932
933 term.append("Two lines above are intentionally left blank.\n");
934 assert_eq!(term.cursor_row(), 4);
935 assert_eq!(term.cursor_col(), 0);
936
937 // Set up the test pattern again, then play with insert/delete
938 term.append("0123456789\n");
939 term.append("ABCDEFGHIJ\n");
940 term.append("abcdefghij\n");
941 assert_eq!(term.cursor_row(), 7);
942
943 term.set_cursor_row(4);
944 term.set_cursor_col(4);
945 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947 term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949 term.cursor_right(5, false);
950 assert_eq!(term.cursor_col(), 9);
951 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953 // Insert two blank rows above cursor. Cursor stays put.
954 term.insert_rows(2);
955 assert_eq!(term.cursor_row(), 4);
956 assert_eq!(term.cursor_col(), 9);
957 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958 term.cursor_down(2, false); // Go down to find our text again
959 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961 // Go back to the beginning of the inserted 'x' characters and delete them.
962 term.cursor_left(5);
963 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964 term.delete_cur_chars(5);
965 assert_eq!(term.cursor_row(), 6);
966 assert_eq!(term.cursor_col(), 4);
967 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969 term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970 term.cursor_down(1, false);
971 term.cursor_left(2);
972 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974 term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975 assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976 term.cursor_up(1, false);
977 term.delete_rows(2); // Delete remains of test pattern
978
979 term.set_text_attrib(Attrib::Bold);
980 term.insert_char_eol('-', 3, 15, 20);
981 term.set_cursor_row(3);
982 term.set_cursor_col(15);
983 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984 assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986 term.set_text_attrib(Attrib::Italic);
987 term.append(" and all lines below");
988 term.set_text_attrib(Attrib::Normal);
989 term.cursor_down(1, false);
990
991 let mut hsb = term.hscrollbar();
992 let mut sb = term.scrollbar();
993 hsb.set_value(100.0);
994 assert_eq!(hsb.value(), 100.0);
995 sb.set_value(50.0);
996 assert_eq!(sb.value(), 50.0);
997 hsb.set_value(0.0);
998 assert_eq!(hsb.value(), 0.0);
999 sb.set_value(0.0);
1000 assert_eq!(sb.value(), 0.0);
1001}
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?
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700 term.take_focus().unwrap();
701
702 // Test the attr_fg_color and attr_bg_color methods.
703 // Put a single character 'A' into the buffer and check it
704 term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705 term.set_text_bg_color(Color::TransparentBg);
706 term.set_text_fg_color(Color::XtermWhite);
707 term.append("A");
708 let r = &term.u8c_disp_row(0);
709 let uc = r.col(0);
710 assert_eq!(uc.text_utf8(), b"A");
711 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712 assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713 assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714 assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715 assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717 // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718 term.clear();
719 term.set_text_fg_color_xterm(XtermColor::White);
720 term.set_text_bg_color_xterm(XtermColor::Black);
721 assert_eq!(term.text_attrib(), Attrib::Normal);
722
723 assert!(term.ansi());
724 term.append("B\x1b[32mC\x1b[1mD\n");
725
726 let r = &term.u8c_disp_row(0);
727 let uc = r.col(0);
728 assert_eq!(uc.text_utf8(), b"B");
729 assert!(uc.is_char(b'B'));
730 assert!(!uc.is_char(b'A'));
731 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735 assert_eq!(
736 &uc.charflags(),
737 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738 );
739
740 let uc = r.col(1);
741 assert_eq!(uc.text_utf8(), b"C");
742 assert!(uc.is_char(b'C'));
743 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747 assert_eq!(
748 &uc.charflags(),
749 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750 );
751
752 let uc = r.col(2);
753 assert_eq!(uc.text_utf8(), b"D");
754 assert!(uc.is_char(b'D'));
755 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757 assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759 assert_eq!(
760 &uc.attr_fgcolor(Some(term)),
761 &Color::from_rgb(0x20, 0xf0, 0x20)
762 );
763 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764 assert_eq!(
765 &uc.charflags(),
766 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767 );
768
769 // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770 term.clear();
771 term.set_text_fg_color_xterm(XtermColor::White);
772 term.set_text_bg_color_xterm(XtermColor::Black);
773 term.set_text_attrib(Attrib::Normal);
774 assert_eq!(term.text_attrib(), Attrib::Normal);
775
776 assert!(term.ansi());
777 term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779 let r = &term.u8c_disp_row(0);
780 let uc = r.col(0);
781 assert_eq!(uc.text_utf8(), b"B");
782 assert!(uc.is_char(b'B'));
783 assert!(!uc.is_char(b'A'));
784 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788 assert_eq!(
789 &uc.charflags(),
790 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791 );
792
793 let uc = r.col(1);
794 assert_eq!(uc.text_utf8(), b"C");
795 assert!(uc.is_char(b'C'));
796 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800 assert_eq!(
801 &uc.charflags(),
802 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803 );
804
805 let uc = r.col(2);
806 assert_eq!(uc.text_utf8(), b"D");
807 assert!(uc.is_char(b'D'));
808 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812 assert_eq!(
813 &uc.charflags(),
814 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815 );
816
817 let uc = r.col(3);
818 assert_eq!(uc.text_utf8(), b"E");
819 assert!(uc.is_char(b'E'));
820 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822 assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823 assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824 assert_eq!(
825 &uc.charflags(),
826 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827 );
828
829 // Test some miscellaneous Utf8 constants
830 assert_eq!(uc.length(), 1);
831 assert_eq!(uc.max_utf8(), 4);
832 assert_eq!(uc.pwidth(), 9.0);
833 assert_eq!(uc.pwidth_int(), 9);
834
835 term.set_text_fg_color_xterm(XtermColor::White);
836 term.set_text_bg_color_xterm(XtermColor::Black);
837 term.clear();
838 term.set_text_attrib(Attrib::Normal);
839
840 // Mouse selection functions
841 term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842 term.clear_mouse_selection();
843 assert_eq!(term.get_selection(), None);
844
845 // Play with cursor position
846 term.append("0123456789\n"); // Set up test pattern
847 term.append("ABCDEFGHIJ\n");
848 term.append("abcdefghij\n");
849
850 term.set_cursor_row(1);
851 assert_eq!(term.cursor_row(), 1);
852 term.set_cursor_col(1);
853 assert_eq!(term.cursor_col(), 1);
854 assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856 term.append("----"); // Overwrites text at cursor and moves cursor forward
857 assert_eq!(term.cursor_row(), 1);
858 assert_eq!(term.cursor_col(), 5);
859 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860 term.set_cursor_col(1);
861 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863 term.cursor_up(1, false);
864 assert_eq!(term.cursor_row(), 0);
865 assert_eq!(term.cursor_col(), 1);
866 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868 // Hit top of screen, so nothing happens
869 term.cursor_up(1, false);
870 assert_eq!(term.cursor_row(), 0);
871 assert_eq!(term.cursor_col(), 1);
872 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874 // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875 term.cursor_up(1, true);
876 assert_eq!(term.cursor_row(), 0);
877 assert_eq!(term.cursor_col(), 1);
878 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880 // Go back down to the overwritten text
881 term.cursor_down(2, false);
882 assert_eq!(term.cursor_row(), 2);
883 assert_eq!(term.cursor_col(), 1);
884 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886 // Go right past the overwritten text
887 term.cursor_right(4, false);
888 assert_eq!(term.cursor_row(), 2);
889 assert_eq!(term.cursor_col(), 5);
890 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892 // Go left to the end of the overwritten text
893 term.cursor_left(1);
894 assert_eq!(term.cursor_row(), 2);
895 assert_eq!(term.cursor_col(), 4);
896 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898 // Scroll back down, removing the blank line at the top.
899 // Cursor stays in place, the text moves under it.
900 term.scroll(1);
901 assert_eq!(term.cursor_row(), 2);
902 assert_eq!(term.cursor_col(), 4);
903 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905 // Clear from here to end-of-line
906 term.clear_eol();
907 assert_eq!(term.cursor_row(), 2);
908 assert_eq!(term.cursor_col(), 4);
909 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911 // Now clear from here to start-of-line. Cursor does not move.
912 term.clear_sol();
913 assert_eq!(term.cursor_row(), 2);
914 assert_eq!(term.cursor_col(), 4);
915 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916 term.cursor_left(1);
917 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918 term.set_cursor_col(0);
919 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921 // Clear some lines
922 term.clear_line(1);
923 assert_eq!(term.cursor_row(), 2);
924 assert_eq!(term.cursor_col(), 0);
925 term.set_cursor_row(1);
926 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927 term.set_cursor_row(3);
928 term.clear_cur_line();
929 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930 assert_eq!(term.cursor_row(), 3);
931 assert_eq!(term.cursor_col(), 0);
932
933 term.append("Two lines above are intentionally left blank.\n");
934 assert_eq!(term.cursor_row(), 4);
935 assert_eq!(term.cursor_col(), 0);
936
937 // Set up the test pattern again, then play with insert/delete
938 term.append("0123456789\n");
939 term.append("ABCDEFGHIJ\n");
940 term.append("abcdefghij\n");
941 assert_eq!(term.cursor_row(), 7);
942
943 term.set_cursor_row(4);
944 term.set_cursor_col(4);
945 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947 term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949 term.cursor_right(5, false);
950 assert_eq!(term.cursor_col(), 9);
951 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953 // Insert two blank rows above cursor. Cursor stays put.
954 term.insert_rows(2);
955 assert_eq!(term.cursor_row(), 4);
956 assert_eq!(term.cursor_col(), 9);
957 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958 term.cursor_down(2, false); // Go down to find our text again
959 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961 // Go back to the beginning of the inserted 'x' characters and delete them.
962 term.cursor_left(5);
963 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964 term.delete_cur_chars(5);
965 assert_eq!(term.cursor_row(), 6);
966 assert_eq!(term.cursor_col(), 4);
967 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969 term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970 term.cursor_down(1, false);
971 term.cursor_left(2);
972 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974 term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975 assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976 term.cursor_up(1, false);
977 term.delete_rows(2); // Delete remains of test pattern
978
979 term.set_text_attrib(Attrib::Bold);
980 term.insert_char_eol('-', 3, 15, 20);
981 term.set_cursor_row(3);
982 term.set_cursor_col(15);
983 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984 assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986 term.set_text_attrib(Attrib::Italic);
987 term.append(" and all lines below");
988 term.set_text_attrib(Attrib::Normal);
989 term.cursor_down(1, false);
990
991 let mut hsb = term.hscrollbar();
992 let mut sb = term.scrollbar();
993 hsb.set_value(100.0);
994 assert_eq!(hsb.value(), 100.0);
995 sb.set_value(50.0);
996 assert_eq!(sb.value(), 50.0);
997 hsb.set_value(0.0);
998 assert_eq!(hsb.value(), 0.0);
999 sb.set_value(0.0);
1000 assert_eq!(sb.value(), 0.0);
1001}
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?
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700 term.take_focus().unwrap();
701
702 // Test the attr_fg_color and attr_bg_color methods.
703 // Put a single character 'A' into the buffer and check it
704 term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705 term.set_text_bg_color(Color::TransparentBg);
706 term.set_text_fg_color(Color::XtermWhite);
707 term.append("A");
708 let r = &term.u8c_disp_row(0);
709 let uc = r.col(0);
710 assert_eq!(uc.text_utf8(), b"A");
711 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712 assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713 assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714 assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715 assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717 // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718 term.clear();
719 term.set_text_fg_color_xterm(XtermColor::White);
720 term.set_text_bg_color_xterm(XtermColor::Black);
721 assert_eq!(term.text_attrib(), Attrib::Normal);
722
723 assert!(term.ansi());
724 term.append("B\x1b[32mC\x1b[1mD\n");
725
726 let r = &term.u8c_disp_row(0);
727 let uc = r.col(0);
728 assert_eq!(uc.text_utf8(), b"B");
729 assert!(uc.is_char(b'B'));
730 assert!(!uc.is_char(b'A'));
731 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735 assert_eq!(
736 &uc.charflags(),
737 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738 );
739
740 let uc = r.col(1);
741 assert_eq!(uc.text_utf8(), b"C");
742 assert!(uc.is_char(b'C'));
743 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747 assert_eq!(
748 &uc.charflags(),
749 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750 );
751
752 let uc = r.col(2);
753 assert_eq!(uc.text_utf8(), b"D");
754 assert!(uc.is_char(b'D'));
755 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757 assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759 assert_eq!(
760 &uc.attr_fgcolor(Some(term)),
761 &Color::from_rgb(0x20, 0xf0, 0x20)
762 );
763 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764 assert_eq!(
765 &uc.charflags(),
766 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767 );
768
769 // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770 term.clear();
771 term.set_text_fg_color_xterm(XtermColor::White);
772 term.set_text_bg_color_xterm(XtermColor::Black);
773 term.set_text_attrib(Attrib::Normal);
774 assert_eq!(term.text_attrib(), Attrib::Normal);
775
776 assert!(term.ansi());
777 term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779 let r = &term.u8c_disp_row(0);
780 let uc = r.col(0);
781 assert_eq!(uc.text_utf8(), b"B");
782 assert!(uc.is_char(b'B'));
783 assert!(!uc.is_char(b'A'));
784 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788 assert_eq!(
789 &uc.charflags(),
790 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791 );
792
793 let uc = r.col(1);
794 assert_eq!(uc.text_utf8(), b"C");
795 assert!(uc.is_char(b'C'));
796 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800 assert_eq!(
801 &uc.charflags(),
802 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803 );
804
805 let uc = r.col(2);
806 assert_eq!(uc.text_utf8(), b"D");
807 assert!(uc.is_char(b'D'));
808 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812 assert_eq!(
813 &uc.charflags(),
814 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815 );
816
817 let uc = r.col(3);
818 assert_eq!(uc.text_utf8(), b"E");
819 assert!(uc.is_char(b'E'));
820 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822 assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823 assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824 assert_eq!(
825 &uc.charflags(),
826 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827 );
828
829 // Test some miscellaneous Utf8 constants
830 assert_eq!(uc.length(), 1);
831 assert_eq!(uc.max_utf8(), 4);
832 assert_eq!(uc.pwidth(), 9.0);
833 assert_eq!(uc.pwidth_int(), 9);
834
835 term.set_text_fg_color_xterm(XtermColor::White);
836 term.set_text_bg_color_xterm(XtermColor::Black);
837 term.clear();
838 term.set_text_attrib(Attrib::Normal);
839
840 // Mouse selection functions
841 term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842 term.clear_mouse_selection();
843 assert_eq!(term.get_selection(), None);
844
845 // Play with cursor position
846 term.append("0123456789\n"); // Set up test pattern
847 term.append("ABCDEFGHIJ\n");
848 term.append("abcdefghij\n");
849
850 term.set_cursor_row(1);
851 assert_eq!(term.cursor_row(), 1);
852 term.set_cursor_col(1);
853 assert_eq!(term.cursor_col(), 1);
854 assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856 term.append("----"); // Overwrites text at cursor and moves cursor forward
857 assert_eq!(term.cursor_row(), 1);
858 assert_eq!(term.cursor_col(), 5);
859 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860 term.set_cursor_col(1);
861 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863 term.cursor_up(1, false);
864 assert_eq!(term.cursor_row(), 0);
865 assert_eq!(term.cursor_col(), 1);
866 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868 // Hit top of screen, so nothing happens
869 term.cursor_up(1, false);
870 assert_eq!(term.cursor_row(), 0);
871 assert_eq!(term.cursor_col(), 1);
872 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874 // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875 term.cursor_up(1, true);
876 assert_eq!(term.cursor_row(), 0);
877 assert_eq!(term.cursor_col(), 1);
878 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880 // Go back down to the overwritten text
881 term.cursor_down(2, false);
882 assert_eq!(term.cursor_row(), 2);
883 assert_eq!(term.cursor_col(), 1);
884 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886 // Go right past the overwritten text
887 term.cursor_right(4, false);
888 assert_eq!(term.cursor_row(), 2);
889 assert_eq!(term.cursor_col(), 5);
890 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892 // Go left to the end of the overwritten text
893 term.cursor_left(1);
894 assert_eq!(term.cursor_row(), 2);
895 assert_eq!(term.cursor_col(), 4);
896 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898 // Scroll back down, removing the blank line at the top.
899 // Cursor stays in place, the text moves under it.
900 term.scroll(1);
901 assert_eq!(term.cursor_row(), 2);
902 assert_eq!(term.cursor_col(), 4);
903 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905 // Clear from here to end-of-line
906 term.clear_eol();
907 assert_eq!(term.cursor_row(), 2);
908 assert_eq!(term.cursor_col(), 4);
909 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911 // Now clear from here to start-of-line. Cursor does not move.
912 term.clear_sol();
913 assert_eq!(term.cursor_row(), 2);
914 assert_eq!(term.cursor_col(), 4);
915 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916 term.cursor_left(1);
917 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918 term.set_cursor_col(0);
919 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921 // Clear some lines
922 term.clear_line(1);
923 assert_eq!(term.cursor_row(), 2);
924 assert_eq!(term.cursor_col(), 0);
925 term.set_cursor_row(1);
926 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927 term.set_cursor_row(3);
928 term.clear_cur_line();
929 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930 assert_eq!(term.cursor_row(), 3);
931 assert_eq!(term.cursor_col(), 0);
932
933 term.append("Two lines above are intentionally left blank.\n");
934 assert_eq!(term.cursor_row(), 4);
935 assert_eq!(term.cursor_col(), 0);
936
937 // Set up the test pattern again, then play with insert/delete
938 term.append("0123456789\n");
939 term.append("ABCDEFGHIJ\n");
940 term.append("abcdefghij\n");
941 assert_eq!(term.cursor_row(), 7);
942
943 term.set_cursor_row(4);
944 term.set_cursor_col(4);
945 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947 term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949 term.cursor_right(5, false);
950 assert_eq!(term.cursor_col(), 9);
951 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953 // Insert two blank rows above cursor. Cursor stays put.
954 term.insert_rows(2);
955 assert_eq!(term.cursor_row(), 4);
956 assert_eq!(term.cursor_col(), 9);
957 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958 term.cursor_down(2, false); // Go down to find our text again
959 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961 // Go back to the beginning of the inserted 'x' characters and delete them.
962 term.cursor_left(5);
963 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964 term.delete_cur_chars(5);
965 assert_eq!(term.cursor_row(), 6);
966 assert_eq!(term.cursor_col(), 4);
967 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969 term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970 term.cursor_down(1, false);
971 term.cursor_left(2);
972 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974 term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975 assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976 term.cursor_up(1, false);
977 term.delete_rows(2); // Delete remains of test pattern
978
979 term.set_text_attrib(Attrib::Bold);
980 term.insert_char_eol('-', 3, 15, 20);
981 term.set_cursor_row(3);
982 term.set_cursor_col(15);
983 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984 assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986 term.set_text_attrib(Attrib::Italic);
987 term.append(" and all lines below");
988 term.set_text_attrib(Attrib::Normal);
989 term.cursor_down(1, false);
990
991 let mut hsb = term.hscrollbar();
992 let mut sb = term.scrollbar();
993 hsb.set_value(100.0);
994 assert_eq!(hsb.value(), 100.0);
995 sb.set_value(50.0);
996 assert_eq!(sb.value(), 50.0);
997 hsb.set_value(0.0);
998 assert_eq!(hsb.value(), 0.0);
999 sb.set_value(0.0);
1000 assert_eq!(sb.value(), 0.0);
1001}
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?
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700 term.take_focus().unwrap();
701
702 // Test the attr_fg_color and attr_bg_color methods.
703 // Put a single character 'A' into the buffer and check it
704 term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705 term.set_text_bg_color(Color::TransparentBg);
706 term.set_text_fg_color(Color::XtermWhite);
707 term.append("A");
708 let r = &term.u8c_disp_row(0);
709 let uc = r.col(0);
710 assert_eq!(uc.text_utf8(), b"A");
711 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712 assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713 assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714 assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715 assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717 // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718 term.clear();
719 term.set_text_fg_color_xterm(XtermColor::White);
720 term.set_text_bg_color_xterm(XtermColor::Black);
721 assert_eq!(term.text_attrib(), Attrib::Normal);
722
723 assert!(term.ansi());
724 term.append("B\x1b[32mC\x1b[1mD\n");
725
726 let r = &term.u8c_disp_row(0);
727 let uc = r.col(0);
728 assert_eq!(uc.text_utf8(), b"B");
729 assert!(uc.is_char(b'B'));
730 assert!(!uc.is_char(b'A'));
731 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735 assert_eq!(
736 &uc.charflags(),
737 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738 );
739
740 let uc = r.col(1);
741 assert_eq!(uc.text_utf8(), b"C");
742 assert!(uc.is_char(b'C'));
743 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747 assert_eq!(
748 &uc.charflags(),
749 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750 );
751
752 let uc = r.col(2);
753 assert_eq!(uc.text_utf8(), b"D");
754 assert!(uc.is_char(b'D'));
755 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757 assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759 assert_eq!(
760 &uc.attr_fgcolor(Some(term)),
761 &Color::from_rgb(0x20, 0xf0, 0x20)
762 );
763 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764 assert_eq!(
765 &uc.charflags(),
766 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767 );
768
769 // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770 term.clear();
771 term.set_text_fg_color_xterm(XtermColor::White);
772 term.set_text_bg_color_xterm(XtermColor::Black);
773 term.set_text_attrib(Attrib::Normal);
774 assert_eq!(term.text_attrib(), Attrib::Normal);
775
776 assert!(term.ansi());
777 term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779 let r = &term.u8c_disp_row(0);
780 let uc = r.col(0);
781 assert_eq!(uc.text_utf8(), b"B");
782 assert!(uc.is_char(b'B'));
783 assert!(!uc.is_char(b'A'));
784 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788 assert_eq!(
789 &uc.charflags(),
790 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791 );
792
793 let uc = r.col(1);
794 assert_eq!(uc.text_utf8(), b"C");
795 assert!(uc.is_char(b'C'));
796 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800 assert_eq!(
801 &uc.charflags(),
802 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803 );
804
805 let uc = r.col(2);
806 assert_eq!(uc.text_utf8(), b"D");
807 assert!(uc.is_char(b'D'));
808 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812 assert_eq!(
813 &uc.charflags(),
814 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815 );
816
817 let uc = r.col(3);
818 assert_eq!(uc.text_utf8(), b"E");
819 assert!(uc.is_char(b'E'));
820 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822 assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823 assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824 assert_eq!(
825 &uc.charflags(),
826 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827 );
828
829 // Test some miscellaneous Utf8 constants
830 assert_eq!(uc.length(), 1);
831 assert_eq!(uc.max_utf8(), 4);
832 assert_eq!(uc.pwidth(), 9.0);
833 assert_eq!(uc.pwidth_int(), 9);
834
835 term.set_text_fg_color_xterm(XtermColor::White);
836 term.set_text_bg_color_xterm(XtermColor::Black);
837 term.clear();
838 term.set_text_attrib(Attrib::Normal);
839
840 // Mouse selection functions
841 term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842 term.clear_mouse_selection();
843 assert_eq!(term.get_selection(), None);
844
845 // Play with cursor position
846 term.append("0123456789\n"); // Set up test pattern
847 term.append("ABCDEFGHIJ\n");
848 term.append("abcdefghij\n");
849
850 term.set_cursor_row(1);
851 assert_eq!(term.cursor_row(), 1);
852 term.set_cursor_col(1);
853 assert_eq!(term.cursor_col(), 1);
854 assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856 term.append("----"); // Overwrites text at cursor and moves cursor forward
857 assert_eq!(term.cursor_row(), 1);
858 assert_eq!(term.cursor_col(), 5);
859 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860 term.set_cursor_col(1);
861 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863 term.cursor_up(1, false);
864 assert_eq!(term.cursor_row(), 0);
865 assert_eq!(term.cursor_col(), 1);
866 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868 // Hit top of screen, so nothing happens
869 term.cursor_up(1, false);
870 assert_eq!(term.cursor_row(), 0);
871 assert_eq!(term.cursor_col(), 1);
872 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874 // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875 term.cursor_up(1, true);
876 assert_eq!(term.cursor_row(), 0);
877 assert_eq!(term.cursor_col(), 1);
878 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880 // Go back down to the overwritten text
881 term.cursor_down(2, false);
882 assert_eq!(term.cursor_row(), 2);
883 assert_eq!(term.cursor_col(), 1);
884 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886 // Go right past the overwritten text
887 term.cursor_right(4, false);
888 assert_eq!(term.cursor_row(), 2);
889 assert_eq!(term.cursor_col(), 5);
890 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892 // Go left to the end of the overwritten text
893 term.cursor_left(1);
894 assert_eq!(term.cursor_row(), 2);
895 assert_eq!(term.cursor_col(), 4);
896 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898 // Scroll back down, removing the blank line at the top.
899 // Cursor stays in place, the text moves under it.
900 term.scroll(1);
901 assert_eq!(term.cursor_row(), 2);
902 assert_eq!(term.cursor_col(), 4);
903 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905 // Clear from here to end-of-line
906 term.clear_eol();
907 assert_eq!(term.cursor_row(), 2);
908 assert_eq!(term.cursor_col(), 4);
909 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911 // Now clear from here to start-of-line. Cursor does not move.
912 term.clear_sol();
913 assert_eq!(term.cursor_row(), 2);
914 assert_eq!(term.cursor_col(), 4);
915 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916 term.cursor_left(1);
917 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918 term.set_cursor_col(0);
919 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921 // Clear some lines
922 term.clear_line(1);
923 assert_eq!(term.cursor_row(), 2);
924 assert_eq!(term.cursor_col(), 0);
925 term.set_cursor_row(1);
926 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927 term.set_cursor_row(3);
928 term.clear_cur_line();
929 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930 assert_eq!(term.cursor_row(), 3);
931 assert_eq!(term.cursor_col(), 0);
932
933 term.append("Two lines above are intentionally left blank.\n");
934 assert_eq!(term.cursor_row(), 4);
935 assert_eq!(term.cursor_col(), 0);
936
937 // Set up the test pattern again, then play with insert/delete
938 term.append("0123456789\n");
939 term.append("ABCDEFGHIJ\n");
940 term.append("abcdefghij\n");
941 assert_eq!(term.cursor_row(), 7);
942
943 term.set_cursor_row(4);
944 term.set_cursor_col(4);
945 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947 term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949 term.cursor_right(5, false);
950 assert_eq!(term.cursor_col(), 9);
951 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953 // Insert two blank rows above cursor. Cursor stays put.
954 term.insert_rows(2);
955 assert_eq!(term.cursor_row(), 4);
956 assert_eq!(term.cursor_col(), 9);
957 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958 term.cursor_down(2, false); // Go down to find our text again
959 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961 // Go back to the beginning of the inserted 'x' characters and delete them.
962 term.cursor_left(5);
963 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964 term.delete_cur_chars(5);
965 assert_eq!(term.cursor_row(), 6);
966 assert_eq!(term.cursor_col(), 4);
967 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969 term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970 term.cursor_down(1, false);
971 term.cursor_left(2);
972 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974 term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975 assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976 term.cursor_up(1, false);
977 term.delete_rows(2); // Delete remains of test pattern
978
979 term.set_text_attrib(Attrib::Bold);
980 term.insert_char_eol('-', 3, 15, 20);
981 term.set_cursor_row(3);
982 term.set_cursor_col(15);
983 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984 assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986 term.set_text_attrib(Attrib::Italic);
987 term.append(" and all lines below");
988 term.set_text_attrib(Attrib::Normal);
989 term.cursor_down(1, false);
990
991 let mut hsb = term.hscrollbar();
992 let mut sb = term.scrollbar();
993 hsb.set_value(100.0);
994 assert_eq!(hsb.value(), 100.0);
995 sb.set_value(50.0);
996 assert_eq!(sb.value(), 50.0);
997 hsb.set_value(0.0);
998 assert_eq!(hsb.value(), 0.0);
999 sb.set_value(0.0);
1000 assert_eq!(sb.value(), 0.0);
1001}
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?
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700 term.take_focus().unwrap();
701
702 // Test the attr_fg_color and attr_bg_color methods.
703 // Put a single character 'A' into the buffer and check it
704 term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705 term.set_text_bg_color(Color::TransparentBg);
706 term.set_text_fg_color(Color::XtermWhite);
707 term.append("A");
708 let r = &term.u8c_disp_row(0);
709 let uc = r.col(0);
710 assert_eq!(uc.text_utf8(), b"A");
711 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712 assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713 assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714 assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715 assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717 // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718 term.clear();
719 term.set_text_fg_color_xterm(XtermColor::White);
720 term.set_text_bg_color_xterm(XtermColor::Black);
721 assert_eq!(term.text_attrib(), Attrib::Normal);
722
723 assert!(term.ansi());
724 term.append("B\x1b[32mC\x1b[1mD\n");
725
726 let r = &term.u8c_disp_row(0);
727 let uc = r.col(0);
728 assert_eq!(uc.text_utf8(), b"B");
729 assert!(uc.is_char(b'B'));
730 assert!(!uc.is_char(b'A'));
731 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735 assert_eq!(
736 &uc.charflags(),
737 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738 );
739
740 let uc = r.col(1);
741 assert_eq!(uc.text_utf8(), b"C");
742 assert!(uc.is_char(b'C'));
743 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747 assert_eq!(
748 &uc.charflags(),
749 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750 );
751
752 let uc = r.col(2);
753 assert_eq!(uc.text_utf8(), b"D");
754 assert!(uc.is_char(b'D'));
755 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757 assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759 assert_eq!(
760 &uc.attr_fgcolor(Some(term)),
761 &Color::from_rgb(0x20, 0xf0, 0x20)
762 );
763 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764 assert_eq!(
765 &uc.charflags(),
766 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767 );
768
769 // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770 term.clear();
771 term.set_text_fg_color_xterm(XtermColor::White);
772 term.set_text_bg_color_xterm(XtermColor::Black);
773 term.set_text_attrib(Attrib::Normal);
774 assert_eq!(term.text_attrib(), Attrib::Normal);
775
776 assert!(term.ansi());
777 term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779 let r = &term.u8c_disp_row(0);
780 let uc = r.col(0);
781 assert_eq!(uc.text_utf8(), b"B");
782 assert!(uc.is_char(b'B'));
783 assert!(!uc.is_char(b'A'));
784 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788 assert_eq!(
789 &uc.charflags(),
790 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791 );
792
793 let uc = r.col(1);
794 assert_eq!(uc.text_utf8(), b"C");
795 assert!(uc.is_char(b'C'));
796 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800 assert_eq!(
801 &uc.charflags(),
802 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803 );
804
805 let uc = r.col(2);
806 assert_eq!(uc.text_utf8(), b"D");
807 assert!(uc.is_char(b'D'));
808 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812 assert_eq!(
813 &uc.charflags(),
814 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815 );
816
817 let uc = r.col(3);
818 assert_eq!(uc.text_utf8(), b"E");
819 assert!(uc.is_char(b'E'));
820 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822 assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823 assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824 assert_eq!(
825 &uc.charflags(),
826 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827 );
828
829 // Test some miscellaneous Utf8 constants
830 assert_eq!(uc.length(), 1);
831 assert_eq!(uc.max_utf8(), 4);
832 assert_eq!(uc.pwidth(), 9.0);
833 assert_eq!(uc.pwidth_int(), 9);
834
835 term.set_text_fg_color_xterm(XtermColor::White);
836 term.set_text_bg_color_xterm(XtermColor::Black);
837 term.clear();
838 term.set_text_attrib(Attrib::Normal);
839
840 // Mouse selection functions
841 term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842 term.clear_mouse_selection();
843 assert_eq!(term.get_selection(), None);
844
845 // Play with cursor position
846 term.append("0123456789\n"); // Set up test pattern
847 term.append("ABCDEFGHIJ\n");
848 term.append("abcdefghij\n");
849
850 term.set_cursor_row(1);
851 assert_eq!(term.cursor_row(), 1);
852 term.set_cursor_col(1);
853 assert_eq!(term.cursor_col(), 1);
854 assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856 term.append("----"); // Overwrites text at cursor and moves cursor forward
857 assert_eq!(term.cursor_row(), 1);
858 assert_eq!(term.cursor_col(), 5);
859 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860 term.set_cursor_col(1);
861 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863 term.cursor_up(1, false);
864 assert_eq!(term.cursor_row(), 0);
865 assert_eq!(term.cursor_col(), 1);
866 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868 // Hit top of screen, so nothing happens
869 term.cursor_up(1, false);
870 assert_eq!(term.cursor_row(), 0);
871 assert_eq!(term.cursor_col(), 1);
872 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874 // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875 term.cursor_up(1, true);
876 assert_eq!(term.cursor_row(), 0);
877 assert_eq!(term.cursor_col(), 1);
878 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880 // Go back down to the overwritten text
881 term.cursor_down(2, false);
882 assert_eq!(term.cursor_row(), 2);
883 assert_eq!(term.cursor_col(), 1);
884 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886 // Go right past the overwritten text
887 term.cursor_right(4, false);
888 assert_eq!(term.cursor_row(), 2);
889 assert_eq!(term.cursor_col(), 5);
890 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892 // Go left to the end of the overwritten text
893 term.cursor_left(1);
894 assert_eq!(term.cursor_row(), 2);
895 assert_eq!(term.cursor_col(), 4);
896 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898 // Scroll back down, removing the blank line at the top.
899 // Cursor stays in place, the text moves under it.
900 term.scroll(1);
901 assert_eq!(term.cursor_row(), 2);
902 assert_eq!(term.cursor_col(), 4);
903 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905 // Clear from here to end-of-line
906 term.clear_eol();
907 assert_eq!(term.cursor_row(), 2);
908 assert_eq!(term.cursor_col(), 4);
909 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911 // Now clear from here to start-of-line. Cursor does not move.
912 term.clear_sol();
913 assert_eq!(term.cursor_row(), 2);
914 assert_eq!(term.cursor_col(), 4);
915 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916 term.cursor_left(1);
917 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918 term.set_cursor_col(0);
919 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921 // Clear some lines
922 term.clear_line(1);
923 assert_eq!(term.cursor_row(), 2);
924 assert_eq!(term.cursor_col(), 0);
925 term.set_cursor_row(1);
926 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927 term.set_cursor_row(3);
928 term.clear_cur_line();
929 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930 assert_eq!(term.cursor_row(), 3);
931 assert_eq!(term.cursor_col(), 0);
932
933 term.append("Two lines above are intentionally left blank.\n");
934 assert_eq!(term.cursor_row(), 4);
935 assert_eq!(term.cursor_col(), 0);
936
937 // Set up the test pattern again, then play with insert/delete
938 term.append("0123456789\n");
939 term.append("ABCDEFGHIJ\n");
940 term.append("abcdefghij\n");
941 assert_eq!(term.cursor_row(), 7);
942
943 term.set_cursor_row(4);
944 term.set_cursor_col(4);
945 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947 term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949 term.cursor_right(5, false);
950 assert_eq!(term.cursor_col(), 9);
951 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953 // Insert two blank rows above cursor. Cursor stays put.
954 term.insert_rows(2);
955 assert_eq!(term.cursor_row(), 4);
956 assert_eq!(term.cursor_col(), 9);
957 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958 term.cursor_down(2, false); // Go down to find our text again
959 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961 // Go back to the beginning of the inserted 'x' characters and delete them.
962 term.cursor_left(5);
963 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964 term.delete_cur_chars(5);
965 assert_eq!(term.cursor_row(), 6);
966 assert_eq!(term.cursor_col(), 4);
967 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969 term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970 term.cursor_down(1, false);
971 term.cursor_left(2);
972 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974 term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975 assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976 term.cursor_up(1, false);
977 term.delete_rows(2); // Delete remains of test pattern
978
979 term.set_text_attrib(Attrib::Bold);
980 term.insert_char_eol('-', 3, 15, 20);
981 term.set_cursor_row(3);
982 term.set_cursor_col(15);
983 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984 assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986 term.set_text_attrib(Attrib::Italic);
987 term.append(" and all lines below");
988 term.set_text_attrib(Attrib::Normal);
989 term.cursor_down(1, false);
990
991 let mut hsb = term.hscrollbar();
992 let mut sb = term.scrollbar();
993 hsb.set_value(100.0);
994 assert_eq!(hsb.value(), 100.0);
995 sb.set_value(50.0);
996 assert_eq!(sb.value(), 50.0);
997 hsb.set_value(0.0);
998 assert_eq!(hsb.value(), 0.0);
999 sb.set_value(0.0);
1000 assert_eq!(sb.value(), 0.0);
1001}
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?
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700 term.take_focus().unwrap();
701
702 // Test the attr_fg_color and attr_bg_color methods.
703 // Put a single character 'A' into the buffer and check it
704 term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705 term.set_text_bg_color(Color::TransparentBg);
706 term.set_text_fg_color(Color::XtermWhite);
707 term.append("A");
708 let r = &term.u8c_disp_row(0);
709 let uc = r.col(0);
710 assert_eq!(uc.text_utf8(), b"A");
711 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712 assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713 assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714 assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715 assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717 // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718 term.clear();
719 term.set_text_fg_color_xterm(XtermColor::White);
720 term.set_text_bg_color_xterm(XtermColor::Black);
721 assert_eq!(term.text_attrib(), Attrib::Normal);
722
723 assert!(term.ansi());
724 term.append("B\x1b[32mC\x1b[1mD\n");
725
726 let r = &term.u8c_disp_row(0);
727 let uc = r.col(0);
728 assert_eq!(uc.text_utf8(), b"B");
729 assert!(uc.is_char(b'B'));
730 assert!(!uc.is_char(b'A'));
731 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735 assert_eq!(
736 &uc.charflags(),
737 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738 );
739
740 let uc = r.col(1);
741 assert_eq!(uc.text_utf8(), b"C");
742 assert!(uc.is_char(b'C'));
743 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747 assert_eq!(
748 &uc.charflags(),
749 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750 );
751
752 let uc = r.col(2);
753 assert_eq!(uc.text_utf8(), b"D");
754 assert!(uc.is_char(b'D'));
755 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757 assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759 assert_eq!(
760 &uc.attr_fgcolor(Some(term)),
761 &Color::from_rgb(0x20, 0xf0, 0x20)
762 );
763 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764 assert_eq!(
765 &uc.charflags(),
766 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767 );
768
769 // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770 term.clear();
771 term.set_text_fg_color_xterm(XtermColor::White);
772 term.set_text_bg_color_xterm(XtermColor::Black);
773 term.set_text_attrib(Attrib::Normal);
774 assert_eq!(term.text_attrib(), Attrib::Normal);
775
776 assert!(term.ansi());
777 term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779 let r = &term.u8c_disp_row(0);
780 let uc = r.col(0);
781 assert_eq!(uc.text_utf8(), b"B");
782 assert!(uc.is_char(b'B'));
783 assert!(!uc.is_char(b'A'));
784 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788 assert_eq!(
789 &uc.charflags(),
790 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791 );
792
793 let uc = r.col(1);
794 assert_eq!(uc.text_utf8(), b"C");
795 assert!(uc.is_char(b'C'));
796 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800 assert_eq!(
801 &uc.charflags(),
802 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803 );
804
805 let uc = r.col(2);
806 assert_eq!(uc.text_utf8(), b"D");
807 assert!(uc.is_char(b'D'));
808 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812 assert_eq!(
813 &uc.charflags(),
814 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815 );
816
817 let uc = r.col(3);
818 assert_eq!(uc.text_utf8(), b"E");
819 assert!(uc.is_char(b'E'));
820 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822 assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823 assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824 assert_eq!(
825 &uc.charflags(),
826 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827 );
828
829 // Test some miscellaneous Utf8 constants
830 assert_eq!(uc.length(), 1);
831 assert_eq!(uc.max_utf8(), 4);
832 assert_eq!(uc.pwidth(), 9.0);
833 assert_eq!(uc.pwidth_int(), 9);
834
835 term.set_text_fg_color_xterm(XtermColor::White);
836 term.set_text_bg_color_xterm(XtermColor::Black);
837 term.clear();
838 term.set_text_attrib(Attrib::Normal);
839
840 // Mouse selection functions
841 term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842 term.clear_mouse_selection();
843 assert_eq!(term.get_selection(), None);
844
845 // Play with cursor position
846 term.append("0123456789\n"); // Set up test pattern
847 term.append("ABCDEFGHIJ\n");
848 term.append("abcdefghij\n");
849
850 term.set_cursor_row(1);
851 assert_eq!(term.cursor_row(), 1);
852 term.set_cursor_col(1);
853 assert_eq!(term.cursor_col(), 1);
854 assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856 term.append("----"); // Overwrites text at cursor and moves cursor forward
857 assert_eq!(term.cursor_row(), 1);
858 assert_eq!(term.cursor_col(), 5);
859 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860 term.set_cursor_col(1);
861 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863 term.cursor_up(1, false);
864 assert_eq!(term.cursor_row(), 0);
865 assert_eq!(term.cursor_col(), 1);
866 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868 // Hit top of screen, so nothing happens
869 term.cursor_up(1, false);
870 assert_eq!(term.cursor_row(), 0);
871 assert_eq!(term.cursor_col(), 1);
872 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874 // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875 term.cursor_up(1, true);
876 assert_eq!(term.cursor_row(), 0);
877 assert_eq!(term.cursor_col(), 1);
878 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880 // Go back down to the overwritten text
881 term.cursor_down(2, false);
882 assert_eq!(term.cursor_row(), 2);
883 assert_eq!(term.cursor_col(), 1);
884 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886 // Go right past the overwritten text
887 term.cursor_right(4, false);
888 assert_eq!(term.cursor_row(), 2);
889 assert_eq!(term.cursor_col(), 5);
890 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892 // Go left to the end of the overwritten text
893 term.cursor_left(1);
894 assert_eq!(term.cursor_row(), 2);
895 assert_eq!(term.cursor_col(), 4);
896 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898 // Scroll back down, removing the blank line at the top.
899 // Cursor stays in place, the text moves under it.
900 term.scroll(1);
901 assert_eq!(term.cursor_row(), 2);
902 assert_eq!(term.cursor_col(), 4);
903 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905 // Clear from here to end-of-line
906 term.clear_eol();
907 assert_eq!(term.cursor_row(), 2);
908 assert_eq!(term.cursor_col(), 4);
909 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911 // Now clear from here to start-of-line. Cursor does not move.
912 term.clear_sol();
913 assert_eq!(term.cursor_row(), 2);
914 assert_eq!(term.cursor_col(), 4);
915 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916 term.cursor_left(1);
917 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918 term.set_cursor_col(0);
919 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921 // Clear some lines
922 term.clear_line(1);
923 assert_eq!(term.cursor_row(), 2);
924 assert_eq!(term.cursor_col(), 0);
925 term.set_cursor_row(1);
926 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927 term.set_cursor_row(3);
928 term.clear_cur_line();
929 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930 assert_eq!(term.cursor_row(), 3);
931 assert_eq!(term.cursor_col(), 0);
932
933 term.append("Two lines above are intentionally left blank.\n");
934 assert_eq!(term.cursor_row(), 4);
935 assert_eq!(term.cursor_col(), 0);
936
937 // Set up the test pattern again, then play with insert/delete
938 term.append("0123456789\n");
939 term.append("ABCDEFGHIJ\n");
940 term.append("abcdefghij\n");
941 assert_eq!(term.cursor_row(), 7);
942
943 term.set_cursor_row(4);
944 term.set_cursor_col(4);
945 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947 term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949 term.cursor_right(5, false);
950 assert_eq!(term.cursor_col(), 9);
951 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953 // Insert two blank rows above cursor. Cursor stays put.
954 term.insert_rows(2);
955 assert_eq!(term.cursor_row(), 4);
956 assert_eq!(term.cursor_col(), 9);
957 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958 term.cursor_down(2, false); // Go down to find our text again
959 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961 // Go back to the beginning of the inserted 'x' characters and delete them.
962 term.cursor_left(5);
963 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964 term.delete_cur_chars(5);
965 assert_eq!(term.cursor_row(), 6);
966 assert_eq!(term.cursor_col(), 4);
967 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969 term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970 term.cursor_down(1, false);
971 term.cursor_left(2);
972 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974 term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975 assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976 term.cursor_up(1, false);
977 term.delete_rows(2); // Delete remains of test pattern
978
979 term.set_text_attrib(Attrib::Bold);
980 term.insert_char_eol('-', 3, 15, 20);
981 term.set_cursor_row(3);
982 term.set_cursor_col(15);
983 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984 assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986 term.set_text_attrib(Attrib::Italic);
987 term.append(" and all lines below");
988 term.set_text_attrib(Attrib::Normal);
989 term.cursor_down(1, false);
990
991 let mut hsb = term.hscrollbar();
992 let mut sb = term.scrollbar();
993 hsb.set_value(100.0);
994 assert_eq!(hsb.value(), 100.0);
995 sb.set_value(50.0);
996 assert_eq!(sb.value(), 50.0);
997 hsb.set_value(0.0);
998 assert_eq!(hsb.value(), 0.0);
999 sb.set_value(0.0);
1000 assert_eq!(sb.value(), 0.0);
1001}
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?
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700 term.take_focus().unwrap();
701
702 // Test the attr_fg_color and attr_bg_color methods.
703 // Put a single character 'A' into the buffer and check it
704 term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705 term.set_text_bg_color(Color::TransparentBg);
706 term.set_text_fg_color(Color::XtermWhite);
707 term.append("A");
708 let r = &term.u8c_disp_row(0);
709 let uc = r.col(0);
710 assert_eq!(uc.text_utf8(), b"A");
711 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712 assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713 assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714 assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715 assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717 // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718 term.clear();
719 term.set_text_fg_color_xterm(XtermColor::White);
720 term.set_text_bg_color_xterm(XtermColor::Black);
721 assert_eq!(term.text_attrib(), Attrib::Normal);
722
723 assert!(term.ansi());
724 term.append("B\x1b[32mC\x1b[1mD\n");
725
726 let r = &term.u8c_disp_row(0);
727 let uc = r.col(0);
728 assert_eq!(uc.text_utf8(), b"B");
729 assert!(uc.is_char(b'B'));
730 assert!(!uc.is_char(b'A'));
731 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735 assert_eq!(
736 &uc.charflags(),
737 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738 );
739
740 let uc = r.col(1);
741 assert_eq!(uc.text_utf8(), b"C");
742 assert!(uc.is_char(b'C'));
743 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747 assert_eq!(
748 &uc.charflags(),
749 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750 );
751
752 let uc = r.col(2);
753 assert_eq!(uc.text_utf8(), b"D");
754 assert!(uc.is_char(b'D'));
755 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757 assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759 assert_eq!(
760 &uc.attr_fgcolor(Some(term)),
761 &Color::from_rgb(0x20, 0xf0, 0x20)
762 );
763 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764 assert_eq!(
765 &uc.charflags(),
766 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767 );
768
769 // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770 term.clear();
771 term.set_text_fg_color_xterm(XtermColor::White);
772 term.set_text_bg_color_xterm(XtermColor::Black);
773 term.set_text_attrib(Attrib::Normal);
774 assert_eq!(term.text_attrib(), Attrib::Normal);
775
776 assert!(term.ansi());
777 term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779 let r = &term.u8c_disp_row(0);
780 let uc = r.col(0);
781 assert_eq!(uc.text_utf8(), b"B");
782 assert!(uc.is_char(b'B'));
783 assert!(!uc.is_char(b'A'));
784 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788 assert_eq!(
789 &uc.charflags(),
790 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791 );
792
793 let uc = r.col(1);
794 assert_eq!(uc.text_utf8(), b"C");
795 assert!(uc.is_char(b'C'));
796 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800 assert_eq!(
801 &uc.charflags(),
802 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803 );
804
805 let uc = r.col(2);
806 assert_eq!(uc.text_utf8(), b"D");
807 assert!(uc.is_char(b'D'));
808 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812 assert_eq!(
813 &uc.charflags(),
814 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815 );
816
817 let uc = r.col(3);
818 assert_eq!(uc.text_utf8(), b"E");
819 assert!(uc.is_char(b'E'));
820 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822 assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823 assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824 assert_eq!(
825 &uc.charflags(),
826 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827 );
828
829 // Test some miscellaneous Utf8 constants
830 assert_eq!(uc.length(), 1);
831 assert_eq!(uc.max_utf8(), 4);
832 assert_eq!(uc.pwidth(), 9.0);
833 assert_eq!(uc.pwidth_int(), 9);
834
835 term.set_text_fg_color_xterm(XtermColor::White);
836 term.set_text_bg_color_xterm(XtermColor::Black);
837 term.clear();
838 term.set_text_attrib(Attrib::Normal);
839
840 // Mouse selection functions
841 term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842 term.clear_mouse_selection();
843 assert_eq!(term.get_selection(), None);
844
845 // Play with cursor position
846 term.append("0123456789\n"); // Set up test pattern
847 term.append("ABCDEFGHIJ\n");
848 term.append("abcdefghij\n");
849
850 term.set_cursor_row(1);
851 assert_eq!(term.cursor_row(), 1);
852 term.set_cursor_col(1);
853 assert_eq!(term.cursor_col(), 1);
854 assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856 term.append("----"); // Overwrites text at cursor and moves cursor forward
857 assert_eq!(term.cursor_row(), 1);
858 assert_eq!(term.cursor_col(), 5);
859 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860 term.set_cursor_col(1);
861 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863 term.cursor_up(1, false);
864 assert_eq!(term.cursor_row(), 0);
865 assert_eq!(term.cursor_col(), 1);
866 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868 // Hit top of screen, so nothing happens
869 term.cursor_up(1, false);
870 assert_eq!(term.cursor_row(), 0);
871 assert_eq!(term.cursor_col(), 1);
872 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874 // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875 term.cursor_up(1, true);
876 assert_eq!(term.cursor_row(), 0);
877 assert_eq!(term.cursor_col(), 1);
878 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880 // Go back down to the overwritten text
881 term.cursor_down(2, false);
882 assert_eq!(term.cursor_row(), 2);
883 assert_eq!(term.cursor_col(), 1);
884 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886 // Go right past the overwritten text
887 term.cursor_right(4, false);
888 assert_eq!(term.cursor_row(), 2);
889 assert_eq!(term.cursor_col(), 5);
890 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892 // Go left to the end of the overwritten text
893 term.cursor_left(1);
894 assert_eq!(term.cursor_row(), 2);
895 assert_eq!(term.cursor_col(), 4);
896 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898 // Scroll back down, removing the blank line at the top.
899 // Cursor stays in place, the text moves under it.
900 term.scroll(1);
901 assert_eq!(term.cursor_row(), 2);
902 assert_eq!(term.cursor_col(), 4);
903 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905 // Clear from here to end-of-line
906 term.clear_eol();
907 assert_eq!(term.cursor_row(), 2);
908 assert_eq!(term.cursor_col(), 4);
909 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911 // Now clear from here to start-of-line. Cursor does not move.
912 term.clear_sol();
913 assert_eq!(term.cursor_row(), 2);
914 assert_eq!(term.cursor_col(), 4);
915 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916 term.cursor_left(1);
917 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918 term.set_cursor_col(0);
919 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921 // Clear some lines
922 term.clear_line(1);
923 assert_eq!(term.cursor_row(), 2);
924 assert_eq!(term.cursor_col(), 0);
925 term.set_cursor_row(1);
926 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927 term.set_cursor_row(3);
928 term.clear_cur_line();
929 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930 assert_eq!(term.cursor_row(), 3);
931 assert_eq!(term.cursor_col(), 0);
932
933 term.append("Two lines above are intentionally left blank.\n");
934 assert_eq!(term.cursor_row(), 4);
935 assert_eq!(term.cursor_col(), 0);
936
937 // Set up the test pattern again, then play with insert/delete
938 term.append("0123456789\n");
939 term.append("ABCDEFGHIJ\n");
940 term.append("abcdefghij\n");
941 assert_eq!(term.cursor_row(), 7);
942
943 term.set_cursor_row(4);
944 term.set_cursor_col(4);
945 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947 term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949 term.cursor_right(5, false);
950 assert_eq!(term.cursor_col(), 9);
951 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953 // Insert two blank rows above cursor. Cursor stays put.
954 term.insert_rows(2);
955 assert_eq!(term.cursor_row(), 4);
956 assert_eq!(term.cursor_col(), 9);
957 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958 term.cursor_down(2, false); // Go down to find our text again
959 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961 // Go back to the beginning of the inserted 'x' characters and delete them.
962 term.cursor_left(5);
963 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964 term.delete_cur_chars(5);
965 assert_eq!(term.cursor_row(), 6);
966 assert_eq!(term.cursor_col(), 4);
967 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969 term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970 term.cursor_down(1, false);
971 term.cursor_left(2);
972 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974 term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975 assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976 term.cursor_up(1, false);
977 term.delete_rows(2); // Delete remains of test pattern
978
979 term.set_text_attrib(Attrib::Bold);
980 term.insert_char_eol('-', 3, 15, 20);
981 term.set_cursor_row(3);
982 term.set_cursor_col(15);
983 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984 assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986 term.set_text_attrib(Attrib::Italic);
987 term.append(" and all lines below");
988 term.set_text_attrib(Attrib::Normal);
989 term.cursor_down(1, false);
990
991 let mut hsb = term.hscrollbar();
992 let mut sb = term.scrollbar();
993 hsb.set_value(100.0);
994 assert_eq!(hsb.value(), 100.0);
995 sb.set_value(50.0);
996 assert_eq!(sb.value(), 50.0);
997 hsb.set_value(0.0);
998 assert_eq!(hsb.value(), 0.0);
999 sb.set_value(0.0);
1000 assert_eq!(sb.value(), 0.0);
1001}
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?
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700 term.take_focus().unwrap();
701
702 // Test the attr_fg_color and attr_bg_color methods.
703 // Put a single character 'A' into the buffer and check it
704 term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705 term.set_text_bg_color(Color::TransparentBg);
706 term.set_text_fg_color(Color::XtermWhite);
707 term.append("A");
708 let r = &term.u8c_disp_row(0);
709 let uc = r.col(0);
710 assert_eq!(uc.text_utf8(), b"A");
711 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712 assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713 assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714 assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715 assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717 // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718 term.clear();
719 term.set_text_fg_color_xterm(XtermColor::White);
720 term.set_text_bg_color_xterm(XtermColor::Black);
721 assert_eq!(term.text_attrib(), Attrib::Normal);
722
723 assert!(term.ansi());
724 term.append("B\x1b[32mC\x1b[1mD\n");
725
726 let r = &term.u8c_disp_row(0);
727 let uc = r.col(0);
728 assert_eq!(uc.text_utf8(), b"B");
729 assert!(uc.is_char(b'B'));
730 assert!(!uc.is_char(b'A'));
731 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735 assert_eq!(
736 &uc.charflags(),
737 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738 );
739
740 let uc = r.col(1);
741 assert_eq!(uc.text_utf8(), b"C");
742 assert!(uc.is_char(b'C'));
743 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747 assert_eq!(
748 &uc.charflags(),
749 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750 );
751
752 let uc = r.col(2);
753 assert_eq!(uc.text_utf8(), b"D");
754 assert!(uc.is_char(b'D'));
755 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757 assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759 assert_eq!(
760 &uc.attr_fgcolor(Some(term)),
761 &Color::from_rgb(0x20, 0xf0, 0x20)
762 );
763 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764 assert_eq!(
765 &uc.charflags(),
766 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767 );
768
769 // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770 term.clear();
771 term.set_text_fg_color_xterm(XtermColor::White);
772 term.set_text_bg_color_xterm(XtermColor::Black);
773 term.set_text_attrib(Attrib::Normal);
774 assert_eq!(term.text_attrib(), Attrib::Normal);
775
776 assert!(term.ansi());
777 term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779 let r = &term.u8c_disp_row(0);
780 let uc = r.col(0);
781 assert_eq!(uc.text_utf8(), b"B");
782 assert!(uc.is_char(b'B'));
783 assert!(!uc.is_char(b'A'));
784 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788 assert_eq!(
789 &uc.charflags(),
790 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791 );
792
793 let uc = r.col(1);
794 assert_eq!(uc.text_utf8(), b"C");
795 assert!(uc.is_char(b'C'));
796 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800 assert_eq!(
801 &uc.charflags(),
802 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803 );
804
805 let uc = r.col(2);
806 assert_eq!(uc.text_utf8(), b"D");
807 assert!(uc.is_char(b'D'));
808 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812 assert_eq!(
813 &uc.charflags(),
814 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815 );
816
817 let uc = r.col(3);
818 assert_eq!(uc.text_utf8(), b"E");
819 assert!(uc.is_char(b'E'));
820 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822 assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823 assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824 assert_eq!(
825 &uc.charflags(),
826 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827 );
828
829 // Test some miscellaneous Utf8 constants
830 assert_eq!(uc.length(), 1);
831 assert_eq!(uc.max_utf8(), 4);
832 assert_eq!(uc.pwidth(), 9.0);
833 assert_eq!(uc.pwidth_int(), 9);
834
835 term.set_text_fg_color_xterm(XtermColor::White);
836 term.set_text_bg_color_xterm(XtermColor::Black);
837 term.clear();
838 term.set_text_attrib(Attrib::Normal);
839
840 // Mouse selection functions
841 term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842 term.clear_mouse_selection();
843 assert_eq!(term.get_selection(), None);
844
845 // Play with cursor position
846 term.append("0123456789\n"); // Set up test pattern
847 term.append("ABCDEFGHIJ\n");
848 term.append("abcdefghij\n");
849
850 term.set_cursor_row(1);
851 assert_eq!(term.cursor_row(), 1);
852 term.set_cursor_col(1);
853 assert_eq!(term.cursor_col(), 1);
854 assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856 term.append("----"); // Overwrites text at cursor and moves cursor forward
857 assert_eq!(term.cursor_row(), 1);
858 assert_eq!(term.cursor_col(), 5);
859 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860 term.set_cursor_col(1);
861 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863 term.cursor_up(1, false);
864 assert_eq!(term.cursor_row(), 0);
865 assert_eq!(term.cursor_col(), 1);
866 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868 // Hit top of screen, so nothing happens
869 term.cursor_up(1, false);
870 assert_eq!(term.cursor_row(), 0);
871 assert_eq!(term.cursor_col(), 1);
872 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874 // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875 term.cursor_up(1, true);
876 assert_eq!(term.cursor_row(), 0);
877 assert_eq!(term.cursor_col(), 1);
878 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880 // Go back down to the overwritten text
881 term.cursor_down(2, false);
882 assert_eq!(term.cursor_row(), 2);
883 assert_eq!(term.cursor_col(), 1);
884 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886 // Go right past the overwritten text
887 term.cursor_right(4, false);
888 assert_eq!(term.cursor_row(), 2);
889 assert_eq!(term.cursor_col(), 5);
890 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892 // Go left to the end of the overwritten text
893 term.cursor_left(1);
894 assert_eq!(term.cursor_row(), 2);
895 assert_eq!(term.cursor_col(), 4);
896 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898 // Scroll back down, removing the blank line at the top.
899 // Cursor stays in place, the text moves under it.
900 term.scroll(1);
901 assert_eq!(term.cursor_row(), 2);
902 assert_eq!(term.cursor_col(), 4);
903 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905 // Clear from here to end-of-line
906 term.clear_eol();
907 assert_eq!(term.cursor_row(), 2);
908 assert_eq!(term.cursor_col(), 4);
909 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911 // Now clear from here to start-of-line. Cursor does not move.
912 term.clear_sol();
913 assert_eq!(term.cursor_row(), 2);
914 assert_eq!(term.cursor_col(), 4);
915 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916 term.cursor_left(1);
917 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918 term.set_cursor_col(0);
919 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921 // Clear some lines
922 term.clear_line(1);
923 assert_eq!(term.cursor_row(), 2);
924 assert_eq!(term.cursor_col(), 0);
925 term.set_cursor_row(1);
926 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927 term.set_cursor_row(3);
928 term.clear_cur_line();
929 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930 assert_eq!(term.cursor_row(), 3);
931 assert_eq!(term.cursor_col(), 0);
932
933 term.append("Two lines above are intentionally left blank.\n");
934 assert_eq!(term.cursor_row(), 4);
935 assert_eq!(term.cursor_col(), 0);
936
937 // Set up the test pattern again, then play with insert/delete
938 term.append("0123456789\n");
939 term.append("ABCDEFGHIJ\n");
940 term.append("abcdefghij\n");
941 assert_eq!(term.cursor_row(), 7);
942
943 term.set_cursor_row(4);
944 term.set_cursor_col(4);
945 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947 term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949 term.cursor_right(5, false);
950 assert_eq!(term.cursor_col(), 9);
951 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953 // Insert two blank rows above cursor. Cursor stays put.
954 term.insert_rows(2);
955 assert_eq!(term.cursor_row(), 4);
956 assert_eq!(term.cursor_col(), 9);
957 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958 term.cursor_down(2, false); // Go down to find our text again
959 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961 // Go back to the beginning of the inserted 'x' characters and delete them.
962 term.cursor_left(5);
963 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964 term.delete_cur_chars(5);
965 assert_eq!(term.cursor_row(), 6);
966 assert_eq!(term.cursor_col(), 4);
967 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969 term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970 term.cursor_down(1, false);
971 term.cursor_left(2);
972 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974 term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975 assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976 term.cursor_up(1, false);
977 term.delete_rows(2); // Delete remains of test pattern
978
979 term.set_text_attrib(Attrib::Bold);
980 term.insert_char_eol('-', 3, 15, 20);
981 term.set_cursor_row(3);
982 term.set_cursor_col(15);
983 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984 assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986 term.set_text_attrib(Attrib::Italic);
987 term.append(" and all lines below");
988 term.set_text_attrib(Attrib::Normal);
989 term.cursor_down(1, false);
990
991 let mut hsb = term.hscrollbar();
992 let mut sb = term.scrollbar();
993 hsb.set_value(100.0);
994 assert_eq!(hsb.value(), 100.0);
995 sb.set_value(50.0);
996 assert_eq!(sb.value(), 50.0);
997 hsb.set_value(0.0);
998 assert_eq!(hsb.value(), 0.0);
999 sb.set_value(0.0);
1000 assert_eq!(sb.value(), 0.0);
1001}
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?
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700 term.take_focus().unwrap();
701
702 // Test the attr_fg_color and attr_bg_color methods.
703 // Put a single character 'A' into the buffer and check it
704 term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705 term.set_text_bg_color(Color::TransparentBg);
706 term.set_text_fg_color(Color::XtermWhite);
707 term.append("A");
708 let r = &term.u8c_disp_row(0);
709 let uc = r.col(0);
710 assert_eq!(uc.text_utf8(), b"A");
711 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712 assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713 assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714 assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715 assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717 // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718 term.clear();
719 term.set_text_fg_color_xterm(XtermColor::White);
720 term.set_text_bg_color_xterm(XtermColor::Black);
721 assert_eq!(term.text_attrib(), Attrib::Normal);
722
723 assert!(term.ansi());
724 term.append("B\x1b[32mC\x1b[1mD\n");
725
726 let r = &term.u8c_disp_row(0);
727 let uc = r.col(0);
728 assert_eq!(uc.text_utf8(), b"B");
729 assert!(uc.is_char(b'B'));
730 assert!(!uc.is_char(b'A'));
731 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735 assert_eq!(
736 &uc.charflags(),
737 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738 );
739
740 let uc = r.col(1);
741 assert_eq!(uc.text_utf8(), b"C");
742 assert!(uc.is_char(b'C'));
743 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747 assert_eq!(
748 &uc.charflags(),
749 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750 );
751
752 let uc = r.col(2);
753 assert_eq!(uc.text_utf8(), b"D");
754 assert!(uc.is_char(b'D'));
755 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757 assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759 assert_eq!(
760 &uc.attr_fgcolor(Some(term)),
761 &Color::from_rgb(0x20, 0xf0, 0x20)
762 );
763 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764 assert_eq!(
765 &uc.charflags(),
766 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767 );
768
769 // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770 term.clear();
771 term.set_text_fg_color_xterm(XtermColor::White);
772 term.set_text_bg_color_xterm(XtermColor::Black);
773 term.set_text_attrib(Attrib::Normal);
774 assert_eq!(term.text_attrib(), Attrib::Normal);
775
776 assert!(term.ansi());
777 term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779 let r = &term.u8c_disp_row(0);
780 let uc = r.col(0);
781 assert_eq!(uc.text_utf8(), b"B");
782 assert!(uc.is_char(b'B'));
783 assert!(!uc.is_char(b'A'));
784 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788 assert_eq!(
789 &uc.charflags(),
790 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791 );
792
793 let uc = r.col(1);
794 assert_eq!(uc.text_utf8(), b"C");
795 assert!(uc.is_char(b'C'));
796 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800 assert_eq!(
801 &uc.charflags(),
802 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803 );
804
805 let uc = r.col(2);
806 assert_eq!(uc.text_utf8(), b"D");
807 assert!(uc.is_char(b'D'));
808 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812 assert_eq!(
813 &uc.charflags(),
814 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815 );
816
817 let uc = r.col(3);
818 assert_eq!(uc.text_utf8(), b"E");
819 assert!(uc.is_char(b'E'));
820 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822 assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823 assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824 assert_eq!(
825 &uc.charflags(),
826 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827 );
828
829 // Test some miscellaneous Utf8 constants
830 assert_eq!(uc.length(), 1);
831 assert_eq!(uc.max_utf8(), 4);
832 assert_eq!(uc.pwidth(), 9.0);
833 assert_eq!(uc.pwidth_int(), 9);
834
835 term.set_text_fg_color_xterm(XtermColor::White);
836 term.set_text_bg_color_xterm(XtermColor::Black);
837 term.clear();
838 term.set_text_attrib(Attrib::Normal);
839
840 // Mouse selection functions
841 term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842 term.clear_mouse_selection();
843 assert_eq!(term.get_selection(), None);
844
845 // Play with cursor position
846 term.append("0123456789\n"); // Set up test pattern
847 term.append("ABCDEFGHIJ\n");
848 term.append("abcdefghij\n");
849
850 term.set_cursor_row(1);
851 assert_eq!(term.cursor_row(), 1);
852 term.set_cursor_col(1);
853 assert_eq!(term.cursor_col(), 1);
854 assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856 term.append("----"); // Overwrites text at cursor and moves cursor forward
857 assert_eq!(term.cursor_row(), 1);
858 assert_eq!(term.cursor_col(), 5);
859 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860 term.set_cursor_col(1);
861 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863 term.cursor_up(1, false);
864 assert_eq!(term.cursor_row(), 0);
865 assert_eq!(term.cursor_col(), 1);
866 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868 // Hit top of screen, so nothing happens
869 term.cursor_up(1, false);
870 assert_eq!(term.cursor_row(), 0);
871 assert_eq!(term.cursor_col(), 1);
872 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874 // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875 term.cursor_up(1, true);
876 assert_eq!(term.cursor_row(), 0);
877 assert_eq!(term.cursor_col(), 1);
878 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880 // Go back down to the overwritten text
881 term.cursor_down(2, false);
882 assert_eq!(term.cursor_row(), 2);
883 assert_eq!(term.cursor_col(), 1);
884 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886 // Go right past the overwritten text
887 term.cursor_right(4, false);
888 assert_eq!(term.cursor_row(), 2);
889 assert_eq!(term.cursor_col(), 5);
890 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892 // Go left to the end of the overwritten text
893 term.cursor_left(1);
894 assert_eq!(term.cursor_row(), 2);
895 assert_eq!(term.cursor_col(), 4);
896 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898 // Scroll back down, removing the blank line at the top.
899 // Cursor stays in place, the text moves under it.
900 term.scroll(1);
901 assert_eq!(term.cursor_row(), 2);
902 assert_eq!(term.cursor_col(), 4);
903 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905 // Clear from here to end-of-line
906 term.clear_eol();
907 assert_eq!(term.cursor_row(), 2);
908 assert_eq!(term.cursor_col(), 4);
909 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911 // Now clear from here to start-of-line. Cursor does not move.
912 term.clear_sol();
913 assert_eq!(term.cursor_row(), 2);
914 assert_eq!(term.cursor_col(), 4);
915 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916 term.cursor_left(1);
917 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918 term.set_cursor_col(0);
919 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921 // Clear some lines
922 term.clear_line(1);
923 assert_eq!(term.cursor_row(), 2);
924 assert_eq!(term.cursor_col(), 0);
925 term.set_cursor_row(1);
926 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927 term.set_cursor_row(3);
928 term.clear_cur_line();
929 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930 assert_eq!(term.cursor_row(), 3);
931 assert_eq!(term.cursor_col(), 0);
932
933 term.append("Two lines above are intentionally left blank.\n");
934 assert_eq!(term.cursor_row(), 4);
935 assert_eq!(term.cursor_col(), 0);
936
937 // Set up the test pattern again, then play with insert/delete
938 term.append("0123456789\n");
939 term.append("ABCDEFGHIJ\n");
940 term.append("abcdefghij\n");
941 assert_eq!(term.cursor_row(), 7);
942
943 term.set_cursor_row(4);
944 term.set_cursor_col(4);
945 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947 term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949 term.cursor_right(5, false);
950 assert_eq!(term.cursor_col(), 9);
951 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953 // Insert two blank rows above cursor. Cursor stays put.
954 term.insert_rows(2);
955 assert_eq!(term.cursor_row(), 4);
956 assert_eq!(term.cursor_col(), 9);
957 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958 term.cursor_down(2, false); // Go down to find our text again
959 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961 // Go back to the beginning of the inserted 'x' characters and delete them.
962 term.cursor_left(5);
963 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964 term.delete_cur_chars(5);
965 assert_eq!(term.cursor_row(), 6);
966 assert_eq!(term.cursor_col(), 4);
967 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969 term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970 term.cursor_down(1, false);
971 term.cursor_left(2);
972 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974 term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975 assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976 term.cursor_up(1, false);
977 term.delete_rows(2); // Delete remains of test pattern
978
979 term.set_text_attrib(Attrib::Bold);
980 term.insert_char_eol('-', 3, 15, 20);
981 term.set_cursor_row(3);
982 term.set_cursor_col(15);
983 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984 assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986 term.set_text_attrib(Attrib::Italic);
987 term.append(" and all lines below");
988 term.set_text_attrib(Attrib::Normal);
989 term.cursor_down(1, false);
990
991 let mut hsb = term.hscrollbar();
992 let mut sb = term.scrollbar();
993 hsb.set_value(100.0);
994 assert_eq!(hsb.value(), 100.0);
995 sb.set_value(50.0);
996 assert_eq!(sb.value(), 50.0);
997 hsb.set_value(0.0);
998 assert_eq!(hsb.value(), 0.0);
999 sb.set_value(0.0);
1000 assert_eq!(sb.value(), 0.0);
1001}
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?
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700 term.take_focus().unwrap();
701
702 // Test the attr_fg_color and attr_bg_color methods.
703 // Put a single character 'A' into the buffer and check it
704 term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705 term.set_text_bg_color(Color::TransparentBg);
706 term.set_text_fg_color(Color::XtermWhite);
707 term.append("A");
708 let r = &term.u8c_disp_row(0);
709 let uc = r.col(0);
710 assert_eq!(uc.text_utf8(), b"A");
711 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712 assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713 assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714 assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715 assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717 // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718 term.clear();
719 term.set_text_fg_color_xterm(XtermColor::White);
720 term.set_text_bg_color_xterm(XtermColor::Black);
721 assert_eq!(term.text_attrib(), Attrib::Normal);
722
723 assert!(term.ansi());
724 term.append("B\x1b[32mC\x1b[1mD\n");
725
726 let r = &term.u8c_disp_row(0);
727 let uc = r.col(0);
728 assert_eq!(uc.text_utf8(), b"B");
729 assert!(uc.is_char(b'B'));
730 assert!(!uc.is_char(b'A'));
731 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735 assert_eq!(
736 &uc.charflags(),
737 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738 );
739
740 let uc = r.col(1);
741 assert_eq!(uc.text_utf8(), b"C");
742 assert!(uc.is_char(b'C'));
743 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747 assert_eq!(
748 &uc.charflags(),
749 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750 );
751
752 let uc = r.col(2);
753 assert_eq!(uc.text_utf8(), b"D");
754 assert!(uc.is_char(b'D'));
755 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757 assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759 assert_eq!(
760 &uc.attr_fgcolor(Some(term)),
761 &Color::from_rgb(0x20, 0xf0, 0x20)
762 );
763 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764 assert_eq!(
765 &uc.charflags(),
766 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767 );
768
769 // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770 term.clear();
771 term.set_text_fg_color_xterm(XtermColor::White);
772 term.set_text_bg_color_xterm(XtermColor::Black);
773 term.set_text_attrib(Attrib::Normal);
774 assert_eq!(term.text_attrib(), Attrib::Normal);
775
776 assert!(term.ansi());
777 term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779 let r = &term.u8c_disp_row(0);
780 let uc = r.col(0);
781 assert_eq!(uc.text_utf8(), b"B");
782 assert!(uc.is_char(b'B'));
783 assert!(!uc.is_char(b'A'));
784 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788 assert_eq!(
789 &uc.charflags(),
790 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791 );
792
793 let uc = r.col(1);
794 assert_eq!(uc.text_utf8(), b"C");
795 assert!(uc.is_char(b'C'));
796 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800 assert_eq!(
801 &uc.charflags(),
802 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803 );
804
805 let uc = r.col(2);
806 assert_eq!(uc.text_utf8(), b"D");
807 assert!(uc.is_char(b'D'));
808 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812 assert_eq!(
813 &uc.charflags(),
814 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815 );
816
817 let uc = r.col(3);
818 assert_eq!(uc.text_utf8(), b"E");
819 assert!(uc.is_char(b'E'));
820 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822 assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823 assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824 assert_eq!(
825 &uc.charflags(),
826 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827 );
828
829 // Test some miscellaneous Utf8 constants
830 assert_eq!(uc.length(), 1);
831 assert_eq!(uc.max_utf8(), 4);
832 assert_eq!(uc.pwidth(), 9.0);
833 assert_eq!(uc.pwidth_int(), 9);
834
835 term.set_text_fg_color_xterm(XtermColor::White);
836 term.set_text_bg_color_xterm(XtermColor::Black);
837 term.clear();
838 term.set_text_attrib(Attrib::Normal);
839
840 // Mouse selection functions
841 term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842 term.clear_mouse_selection();
843 assert_eq!(term.get_selection(), None);
844
845 // Play with cursor position
846 term.append("0123456789\n"); // Set up test pattern
847 term.append("ABCDEFGHIJ\n");
848 term.append("abcdefghij\n");
849
850 term.set_cursor_row(1);
851 assert_eq!(term.cursor_row(), 1);
852 term.set_cursor_col(1);
853 assert_eq!(term.cursor_col(), 1);
854 assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856 term.append("----"); // Overwrites text at cursor and moves cursor forward
857 assert_eq!(term.cursor_row(), 1);
858 assert_eq!(term.cursor_col(), 5);
859 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860 term.set_cursor_col(1);
861 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863 term.cursor_up(1, false);
864 assert_eq!(term.cursor_row(), 0);
865 assert_eq!(term.cursor_col(), 1);
866 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868 // Hit top of screen, so nothing happens
869 term.cursor_up(1, false);
870 assert_eq!(term.cursor_row(), 0);
871 assert_eq!(term.cursor_col(), 1);
872 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874 // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875 term.cursor_up(1, true);
876 assert_eq!(term.cursor_row(), 0);
877 assert_eq!(term.cursor_col(), 1);
878 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880 // Go back down to the overwritten text
881 term.cursor_down(2, false);
882 assert_eq!(term.cursor_row(), 2);
883 assert_eq!(term.cursor_col(), 1);
884 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886 // Go right past the overwritten text
887 term.cursor_right(4, false);
888 assert_eq!(term.cursor_row(), 2);
889 assert_eq!(term.cursor_col(), 5);
890 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892 // Go left to the end of the overwritten text
893 term.cursor_left(1);
894 assert_eq!(term.cursor_row(), 2);
895 assert_eq!(term.cursor_col(), 4);
896 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898 // Scroll back down, removing the blank line at the top.
899 // Cursor stays in place, the text moves under it.
900 term.scroll(1);
901 assert_eq!(term.cursor_row(), 2);
902 assert_eq!(term.cursor_col(), 4);
903 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905 // Clear from here to end-of-line
906 term.clear_eol();
907 assert_eq!(term.cursor_row(), 2);
908 assert_eq!(term.cursor_col(), 4);
909 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911 // Now clear from here to start-of-line. Cursor does not move.
912 term.clear_sol();
913 assert_eq!(term.cursor_row(), 2);
914 assert_eq!(term.cursor_col(), 4);
915 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916 term.cursor_left(1);
917 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918 term.set_cursor_col(0);
919 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921 // Clear some lines
922 term.clear_line(1);
923 assert_eq!(term.cursor_row(), 2);
924 assert_eq!(term.cursor_col(), 0);
925 term.set_cursor_row(1);
926 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927 term.set_cursor_row(3);
928 term.clear_cur_line();
929 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930 assert_eq!(term.cursor_row(), 3);
931 assert_eq!(term.cursor_col(), 0);
932
933 term.append("Two lines above are intentionally left blank.\n");
934 assert_eq!(term.cursor_row(), 4);
935 assert_eq!(term.cursor_col(), 0);
936
937 // Set up the test pattern again, then play with insert/delete
938 term.append("0123456789\n");
939 term.append("ABCDEFGHIJ\n");
940 term.append("abcdefghij\n");
941 assert_eq!(term.cursor_row(), 7);
942
943 term.set_cursor_row(4);
944 term.set_cursor_col(4);
945 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947 term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949 term.cursor_right(5, false);
950 assert_eq!(term.cursor_col(), 9);
951 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953 // Insert two blank rows above cursor. Cursor stays put.
954 term.insert_rows(2);
955 assert_eq!(term.cursor_row(), 4);
956 assert_eq!(term.cursor_col(), 9);
957 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958 term.cursor_down(2, false); // Go down to find our text again
959 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961 // Go back to the beginning of the inserted 'x' characters and delete them.
962 term.cursor_left(5);
963 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964 term.delete_cur_chars(5);
965 assert_eq!(term.cursor_row(), 6);
966 assert_eq!(term.cursor_col(), 4);
967 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969 term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970 term.cursor_down(1, false);
971 term.cursor_left(2);
972 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974 term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975 assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976 term.cursor_up(1, false);
977 term.delete_rows(2); // Delete remains of test pattern
978
979 term.set_text_attrib(Attrib::Bold);
980 term.insert_char_eol('-', 3, 15, 20);
981 term.set_cursor_row(3);
982 term.set_cursor_col(15);
983 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984 assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986 term.set_text_attrib(Attrib::Italic);
987 term.append(" and all lines below");
988 term.set_text_attrib(Attrib::Normal);
989 term.cursor_down(1, false);
990
991 let mut hsb = term.hscrollbar();
992 let mut sb = term.scrollbar();
993 hsb.set_value(100.0);
994 assert_eq!(hsb.value(), 100.0);
995 sb.set_value(50.0);
996 assert_eq!(sb.value(), 50.0);
997 hsb.set_value(0.0);
998 assert_eq!(hsb.value(), 0.0);
999 sb.set_value(0.0);
1000 assert_eq!(sb.value(), 0.0);
1001}
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?
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700 term.take_focus().unwrap();
701
702 // Test the attr_fg_color and attr_bg_color methods.
703 // Put a single character 'A' into the buffer and check it
704 term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705 term.set_text_bg_color(Color::TransparentBg);
706 term.set_text_fg_color(Color::XtermWhite);
707 term.append("A");
708 let r = &term.u8c_disp_row(0);
709 let uc = r.col(0);
710 assert_eq!(uc.text_utf8(), b"A");
711 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712 assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713 assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714 assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715 assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717 // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718 term.clear();
719 term.set_text_fg_color_xterm(XtermColor::White);
720 term.set_text_bg_color_xterm(XtermColor::Black);
721 assert_eq!(term.text_attrib(), Attrib::Normal);
722
723 assert!(term.ansi());
724 term.append("B\x1b[32mC\x1b[1mD\n");
725
726 let r = &term.u8c_disp_row(0);
727 let uc = r.col(0);
728 assert_eq!(uc.text_utf8(), b"B");
729 assert!(uc.is_char(b'B'));
730 assert!(!uc.is_char(b'A'));
731 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735 assert_eq!(
736 &uc.charflags(),
737 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738 );
739
740 let uc = r.col(1);
741 assert_eq!(uc.text_utf8(), b"C");
742 assert!(uc.is_char(b'C'));
743 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747 assert_eq!(
748 &uc.charflags(),
749 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750 );
751
752 let uc = r.col(2);
753 assert_eq!(uc.text_utf8(), b"D");
754 assert!(uc.is_char(b'D'));
755 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757 assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759 assert_eq!(
760 &uc.attr_fgcolor(Some(term)),
761 &Color::from_rgb(0x20, 0xf0, 0x20)
762 );
763 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764 assert_eq!(
765 &uc.charflags(),
766 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767 );
768
769 // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770 term.clear();
771 term.set_text_fg_color_xterm(XtermColor::White);
772 term.set_text_bg_color_xterm(XtermColor::Black);
773 term.set_text_attrib(Attrib::Normal);
774 assert_eq!(term.text_attrib(), Attrib::Normal);
775
776 assert!(term.ansi());
777 term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779 let r = &term.u8c_disp_row(0);
780 let uc = r.col(0);
781 assert_eq!(uc.text_utf8(), b"B");
782 assert!(uc.is_char(b'B'));
783 assert!(!uc.is_char(b'A'));
784 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788 assert_eq!(
789 &uc.charflags(),
790 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791 );
792
793 let uc = r.col(1);
794 assert_eq!(uc.text_utf8(), b"C");
795 assert!(uc.is_char(b'C'));
796 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800 assert_eq!(
801 &uc.charflags(),
802 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803 );
804
805 let uc = r.col(2);
806 assert_eq!(uc.text_utf8(), b"D");
807 assert!(uc.is_char(b'D'));
808 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812 assert_eq!(
813 &uc.charflags(),
814 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815 );
816
817 let uc = r.col(3);
818 assert_eq!(uc.text_utf8(), b"E");
819 assert!(uc.is_char(b'E'));
820 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822 assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823 assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824 assert_eq!(
825 &uc.charflags(),
826 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827 );
828
829 // Test some miscellaneous Utf8 constants
830 assert_eq!(uc.length(), 1);
831 assert_eq!(uc.max_utf8(), 4);
832 assert_eq!(uc.pwidth(), 9.0);
833 assert_eq!(uc.pwidth_int(), 9);
834
835 term.set_text_fg_color_xterm(XtermColor::White);
836 term.set_text_bg_color_xterm(XtermColor::Black);
837 term.clear();
838 term.set_text_attrib(Attrib::Normal);
839
840 // Mouse selection functions
841 term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842 term.clear_mouse_selection();
843 assert_eq!(term.get_selection(), None);
844
845 // Play with cursor position
846 term.append("0123456789\n"); // Set up test pattern
847 term.append("ABCDEFGHIJ\n");
848 term.append("abcdefghij\n");
849
850 term.set_cursor_row(1);
851 assert_eq!(term.cursor_row(), 1);
852 term.set_cursor_col(1);
853 assert_eq!(term.cursor_col(), 1);
854 assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856 term.append("----"); // Overwrites text at cursor and moves cursor forward
857 assert_eq!(term.cursor_row(), 1);
858 assert_eq!(term.cursor_col(), 5);
859 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860 term.set_cursor_col(1);
861 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863 term.cursor_up(1, false);
864 assert_eq!(term.cursor_row(), 0);
865 assert_eq!(term.cursor_col(), 1);
866 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868 // Hit top of screen, so nothing happens
869 term.cursor_up(1, false);
870 assert_eq!(term.cursor_row(), 0);
871 assert_eq!(term.cursor_col(), 1);
872 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874 // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875 term.cursor_up(1, true);
876 assert_eq!(term.cursor_row(), 0);
877 assert_eq!(term.cursor_col(), 1);
878 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880 // Go back down to the overwritten text
881 term.cursor_down(2, false);
882 assert_eq!(term.cursor_row(), 2);
883 assert_eq!(term.cursor_col(), 1);
884 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886 // Go right past the overwritten text
887 term.cursor_right(4, false);
888 assert_eq!(term.cursor_row(), 2);
889 assert_eq!(term.cursor_col(), 5);
890 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892 // Go left to the end of the overwritten text
893 term.cursor_left(1);
894 assert_eq!(term.cursor_row(), 2);
895 assert_eq!(term.cursor_col(), 4);
896 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898 // Scroll back down, removing the blank line at the top.
899 // Cursor stays in place, the text moves under it.
900 term.scroll(1);
901 assert_eq!(term.cursor_row(), 2);
902 assert_eq!(term.cursor_col(), 4);
903 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905 // Clear from here to end-of-line
906 term.clear_eol();
907 assert_eq!(term.cursor_row(), 2);
908 assert_eq!(term.cursor_col(), 4);
909 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911 // Now clear from here to start-of-line. Cursor does not move.
912 term.clear_sol();
913 assert_eq!(term.cursor_row(), 2);
914 assert_eq!(term.cursor_col(), 4);
915 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916 term.cursor_left(1);
917 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918 term.set_cursor_col(0);
919 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921 // Clear some lines
922 term.clear_line(1);
923 assert_eq!(term.cursor_row(), 2);
924 assert_eq!(term.cursor_col(), 0);
925 term.set_cursor_row(1);
926 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927 term.set_cursor_row(3);
928 term.clear_cur_line();
929 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930 assert_eq!(term.cursor_row(), 3);
931 assert_eq!(term.cursor_col(), 0);
932
933 term.append("Two lines above are intentionally left blank.\n");
934 assert_eq!(term.cursor_row(), 4);
935 assert_eq!(term.cursor_col(), 0);
936
937 // Set up the test pattern again, then play with insert/delete
938 term.append("0123456789\n");
939 term.append("ABCDEFGHIJ\n");
940 term.append("abcdefghij\n");
941 assert_eq!(term.cursor_row(), 7);
942
943 term.set_cursor_row(4);
944 term.set_cursor_col(4);
945 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947 term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949 term.cursor_right(5, false);
950 assert_eq!(term.cursor_col(), 9);
951 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953 // Insert two blank rows above cursor. Cursor stays put.
954 term.insert_rows(2);
955 assert_eq!(term.cursor_row(), 4);
956 assert_eq!(term.cursor_col(), 9);
957 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958 term.cursor_down(2, false); // Go down to find our text again
959 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961 // Go back to the beginning of the inserted 'x' characters and delete them.
962 term.cursor_left(5);
963 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964 term.delete_cur_chars(5);
965 assert_eq!(term.cursor_row(), 6);
966 assert_eq!(term.cursor_col(), 4);
967 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969 term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970 term.cursor_down(1, false);
971 term.cursor_left(2);
972 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974 term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975 assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976 term.cursor_up(1, false);
977 term.delete_rows(2); // Delete remains of test pattern
978
979 term.set_text_attrib(Attrib::Bold);
980 term.insert_char_eol('-', 3, 15, 20);
981 term.set_cursor_row(3);
982 term.set_cursor_col(15);
983 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984 assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986 term.set_text_attrib(Attrib::Italic);
987 term.append(" and all lines below");
988 term.set_text_attrib(Attrib::Normal);
989 term.cursor_down(1, false);
990
991 let mut hsb = term.hscrollbar();
992 let mut sb = term.scrollbar();
993 hsb.set_value(100.0);
994 assert_eq!(hsb.value(), 100.0);
995 sb.set_value(50.0);
996 assert_eq!(sb.value(), 50.0);
997 hsb.set_value(0.0);
998 assert_eq!(hsb.value(), 0.0);
999 sb.set_value(0.0);
1000 assert_eq!(sb.value(), 0.0);
1001}
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?
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700 term.take_focus().unwrap();
701
702 // Test the attr_fg_color and attr_bg_color methods.
703 // Put a single character 'A' into the buffer and check it
704 term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705 term.set_text_bg_color(Color::TransparentBg);
706 term.set_text_fg_color(Color::XtermWhite);
707 term.append("A");
708 let r = &term.u8c_disp_row(0);
709 let uc = r.col(0);
710 assert_eq!(uc.text_utf8(), b"A");
711 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712 assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713 assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714 assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715 assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717 // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718 term.clear();
719 term.set_text_fg_color_xterm(XtermColor::White);
720 term.set_text_bg_color_xterm(XtermColor::Black);
721 assert_eq!(term.text_attrib(), Attrib::Normal);
722
723 assert!(term.ansi());
724 term.append("B\x1b[32mC\x1b[1mD\n");
725
726 let r = &term.u8c_disp_row(0);
727 let uc = r.col(0);
728 assert_eq!(uc.text_utf8(), b"B");
729 assert!(uc.is_char(b'B'));
730 assert!(!uc.is_char(b'A'));
731 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735 assert_eq!(
736 &uc.charflags(),
737 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738 );
739
740 let uc = r.col(1);
741 assert_eq!(uc.text_utf8(), b"C");
742 assert!(uc.is_char(b'C'));
743 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747 assert_eq!(
748 &uc.charflags(),
749 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750 );
751
752 let uc = r.col(2);
753 assert_eq!(uc.text_utf8(), b"D");
754 assert!(uc.is_char(b'D'));
755 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757 assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759 assert_eq!(
760 &uc.attr_fgcolor(Some(term)),
761 &Color::from_rgb(0x20, 0xf0, 0x20)
762 );
763 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764 assert_eq!(
765 &uc.charflags(),
766 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767 );
768
769 // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770 term.clear();
771 term.set_text_fg_color_xterm(XtermColor::White);
772 term.set_text_bg_color_xterm(XtermColor::Black);
773 term.set_text_attrib(Attrib::Normal);
774 assert_eq!(term.text_attrib(), Attrib::Normal);
775
776 assert!(term.ansi());
777 term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779 let r = &term.u8c_disp_row(0);
780 let uc = r.col(0);
781 assert_eq!(uc.text_utf8(), b"B");
782 assert!(uc.is_char(b'B'));
783 assert!(!uc.is_char(b'A'));
784 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788 assert_eq!(
789 &uc.charflags(),
790 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791 );
792
793 let uc = r.col(1);
794 assert_eq!(uc.text_utf8(), b"C");
795 assert!(uc.is_char(b'C'));
796 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800 assert_eq!(
801 &uc.charflags(),
802 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803 );
804
805 let uc = r.col(2);
806 assert_eq!(uc.text_utf8(), b"D");
807 assert!(uc.is_char(b'D'));
808 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812 assert_eq!(
813 &uc.charflags(),
814 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815 );
816
817 let uc = r.col(3);
818 assert_eq!(uc.text_utf8(), b"E");
819 assert!(uc.is_char(b'E'));
820 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822 assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823 assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824 assert_eq!(
825 &uc.charflags(),
826 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827 );
828
829 // Test some miscellaneous Utf8 constants
830 assert_eq!(uc.length(), 1);
831 assert_eq!(uc.max_utf8(), 4);
832 assert_eq!(uc.pwidth(), 9.0);
833 assert_eq!(uc.pwidth_int(), 9);
834
835 term.set_text_fg_color_xterm(XtermColor::White);
836 term.set_text_bg_color_xterm(XtermColor::Black);
837 term.clear();
838 term.set_text_attrib(Attrib::Normal);
839
840 // Mouse selection functions
841 term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842 term.clear_mouse_selection();
843 assert_eq!(term.get_selection(), None);
844
845 // Play with cursor position
846 term.append("0123456789\n"); // Set up test pattern
847 term.append("ABCDEFGHIJ\n");
848 term.append("abcdefghij\n");
849
850 term.set_cursor_row(1);
851 assert_eq!(term.cursor_row(), 1);
852 term.set_cursor_col(1);
853 assert_eq!(term.cursor_col(), 1);
854 assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856 term.append("----"); // Overwrites text at cursor and moves cursor forward
857 assert_eq!(term.cursor_row(), 1);
858 assert_eq!(term.cursor_col(), 5);
859 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860 term.set_cursor_col(1);
861 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863 term.cursor_up(1, false);
864 assert_eq!(term.cursor_row(), 0);
865 assert_eq!(term.cursor_col(), 1);
866 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868 // Hit top of screen, so nothing happens
869 term.cursor_up(1, false);
870 assert_eq!(term.cursor_row(), 0);
871 assert_eq!(term.cursor_col(), 1);
872 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874 // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875 term.cursor_up(1, true);
876 assert_eq!(term.cursor_row(), 0);
877 assert_eq!(term.cursor_col(), 1);
878 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880 // Go back down to the overwritten text
881 term.cursor_down(2, false);
882 assert_eq!(term.cursor_row(), 2);
883 assert_eq!(term.cursor_col(), 1);
884 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886 // Go right past the overwritten text
887 term.cursor_right(4, false);
888 assert_eq!(term.cursor_row(), 2);
889 assert_eq!(term.cursor_col(), 5);
890 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892 // Go left to the end of the overwritten text
893 term.cursor_left(1);
894 assert_eq!(term.cursor_row(), 2);
895 assert_eq!(term.cursor_col(), 4);
896 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898 // Scroll back down, removing the blank line at the top.
899 // Cursor stays in place, the text moves under it.
900 term.scroll(1);
901 assert_eq!(term.cursor_row(), 2);
902 assert_eq!(term.cursor_col(), 4);
903 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905 // Clear from here to end-of-line
906 term.clear_eol();
907 assert_eq!(term.cursor_row(), 2);
908 assert_eq!(term.cursor_col(), 4);
909 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911 // Now clear from here to start-of-line. Cursor does not move.
912 term.clear_sol();
913 assert_eq!(term.cursor_row(), 2);
914 assert_eq!(term.cursor_col(), 4);
915 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916 term.cursor_left(1);
917 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918 term.set_cursor_col(0);
919 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921 // Clear some lines
922 term.clear_line(1);
923 assert_eq!(term.cursor_row(), 2);
924 assert_eq!(term.cursor_col(), 0);
925 term.set_cursor_row(1);
926 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927 term.set_cursor_row(3);
928 term.clear_cur_line();
929 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930 assert_eq!(term.cursor_row(), 3);
931 assert_eq!(term.cursor_col(), 0);
932
933 term.append("Two lines above are intentionally left blank.\n");
934 assert_eq!(term.cursor_row(), 4);
935 assert_eq!(term.cursor_col(), 0);
936
937 // Set up the test pattern again, then play with insert/delete
938 term.append("0123456789\n");
939 term.append("ABCDEFGHIJ\n");
940 term.append("abcdefghij\n");
941 assert_eq!(term.cursor_row(), 7);
942
943 term.set_cursor_row(4);
944 term.set_cursor_col(4);
945 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947 term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949 term.cursor_right(5, false);
950 assert_eq!(term.cursor_col(), 9);
951 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953 // Insert two blank rows above cursor. Cursor stays put.
954 term.insert_rows(2);
955 assert_eq!(term.cursor_row(), 4);
956 assert_eq!(term.cursor_col(), 9);
957 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958 term.cursor_down(2, false); // Go down to find our text again
959 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961 // Go back to the beginning of the inserted 'x' characters and delete them.
962 term.cursor_left(5);
963 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964 term.delete_cur_chars(5);
965 assert_eq!(term.cursor_row(), 6);
966 assert_eq!(term.cursor_col(), 4);
967 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969 term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970 term.cursor_down(1, false);
971 term.cursor_left(2);
972 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974 term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975 assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976 term.cursor_up(1, false);
977 term.delete_rows(2); // Delete remains of test pattern
978
979 term.set_text_attrib(Attrib::Bold);
980 term.insert_char_eol('-', 3, 15, 20);
981 term.set_cursor_row(3);
982 term.set_cursor_col(15);
983 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984 assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986 term.set_text_attrib(Attrib::Italic);
987 term.append(" and all lines below");
988 term.set_text_attrib(Attrib::Normal);
989 term.cursor_down(1, false);
990
991 let mut hsb = term.hscrollbar();
992 let mut sb = term.scrollbar();
993 hsb.set_value(100.0);
994 assert_eq!(hsb.value(), 100.0);
995 sb.set_value(50.0);
996 assert_eq!(sb.value(), 50.0);
997 hsb.set_value(0.0);
998 assert_eq!(hsb.value(), 0.0);
999 sb.set_value(0.0);
1000 assert_eq!(sb.value(), 0.0);
1001}
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?
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700 term.take_focus().unwrap();
701
702 // Test the attr_fg_color and attr_bg_color methods.
703 // Put a single character 'A' into the buffer and check it
704 term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705 term.set_text_bg_color(Color::TransparentBg);
706 term.set_text_fg_color(Color::XtermWhite);
707 term.append("A");
708 let r = &term.u8c_disp_row(0);
709 let uc = r.col(0);
710 assert_eq!(uc.text_utf8(), b"A");
711 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712 assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713 assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714 assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715 assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717 // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718 term.clear();
719 term.set_text_fg_color_xterm(XtermColor::White);
720 term.set_text_bg_color_xterm(XtermColor::Black);
721 assert_eq!(term.text_attrib(), Attrib::Normal);
722
723 assert!(term.ansi());
724 term.append("B\x1b[32mC\x1b[1mD\n");
725
726 let r = &term.u8c_disp_row(0);
727 let uc = r.col(0);
728 assert_eq!(uc.text_utf8(), b"B");
729 assert!(uc.is_char(b'B'));
730 assert!(!uc.is_char(b'A'));
731 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735 assert_eq!(
736 &uc.charflags(),
737 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738 );
739
740 let uc = r.col(1);
741 assert_eq!(uc.text_utf8(), b"C");
742 assert!(uc.is_char(b'C'));
743 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747 assert_eq!(
748 &uc.charflags(),
749 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750 );
751
752 let uc = r.col(2);
753 assert_eq!(uc.text_utf8(), b"D");
754 assert!(uc.is_char(b'D'));
755 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757 assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759 assert_eq!(
760 &uc.attr_fgcolor(Some(term)),
761 &Color::from_rgb(0x20, 0xf0, 0x20)
762 );
763 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764 assert_eq!(
765 &uc.charflags(),
766 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767 );
768
769 // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770 term.clear();
771 term.set_text_fg_color_xterm(XtermColor::White);
772 term.set_text_bg_color_xterm(XtermColor::Black);
773 term.set_text_attrib(Attrib::Normal);
774 assert_eq!(term.text_attrib(), Attrib::Normal);
775
776 assert!(term.ansi());
777 term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779 let r = &term.u8c_disp_row(0);
780 let uc = r.col(0);
781 assert_eq!(uc.text_utf8(), b"B");
782 assert!(uc.is_char(b'B'));
783 assert!(!uc.is_char(b'A'));
784 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788 assert_eq!(
789 &uc.charflags(),
790 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791 );
792
793 let uc = r.col(1);
794 assert_eq!(uc.text_utf8(), b"C");
795 assert!(uc.is_char(b'C'));
796 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800 assert_eq!(
801 &uc.charflags(),
802 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803 );
804
805 let uc = r.col(2);
806 assert_eq!(uc.text_utf8(), b"D");
807 assert!(uc.is_char(b'D'));
808 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812 assert_eq!(
813 &uc.charflags(),
814 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815 );
816
817 let uc = r.col(3);
818 assert_eq!(uc.text_utf8(), b"E");
819 assert!(uc.is_char(b'E'));
820 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822 assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823 assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824 assert_eq!(
825 &uc.charflags(),
826 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827 );
828
829 // Test some miscellaneous Utf8 constants
830 assert_eq!(uc.length(), 1);
831 assert_eq!(uc.max_utf8(), 4);
832 assert_eq!(uc.pwidth(), 9.0);
833 assert_eq!(uc.pwidth_int(), 9);
834
835 term.set_text_fg_color_xterm(XtermColor::White);
836 term.set_text_bg_color_xterm(XtermColor::Black);
837 term.clear();
838 term.set_text_attrib(Attrib::Normal);
839
840 // Mouse selection functions
841 term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842 term.clear_mouse_selection();
843 assert_eq!(term.get_selection(), None);
844
845 // Play with cursor position
846 term.append("0123456789\n"); // Set up test pattern
847 term.append("ABCDEFGHIJ\n");
848 term.append("abcdefghij\n");
849
850 term.set_cursor_row(1);
851 assert_eq!(term.cursor_row(), 1);
852 term.set_cursor_col(1);
853 assert_eq!(term.cursor_col(), 1);
854 assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856 term.append("----"); // Overwrites text at cursor and moves cursor forward
857 assert_eq!(term.cursor_row(), 1);
858 assert_eq!(term.cursor_col(), 5);
859 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860 term.set_cursor_col(1);
861 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863 term.cursor_up(1, false);
864 assert_eq!(term.cursor_row(), 0);
865 assert_eq!(term.cursor_col(), 1);
866 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868 // Hit top of screen, so nothing happens
869 term.cursor_up(1, false);
870 assert_eq!(term.cursor_row(), 0);
871 assert_eq!(term.cursor_col(), 1);
872 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874 // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875 term.cursor_up(1, true);
876 assert_eq!(term.cursor_row(), 0);
877 assert_eq!(term.cursor_col(), 1);
878 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880 // Go back down to the overwritten text
881 term.cursor_down(2, false);
882 assert_eq!(term.cursor_row(), 2);
883 assert_eq!(term.cursor_col(), 1);
884 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886 // Go right past the overwritten text
887 term.cursor_right(4, false);
888 assert_eq!(term.cursor_row(), 2);
889 assert_eq!(term.cursor_col(), 5);
890 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892 // Go left to the end of the overwritten text
893 term.cursor_left(1);
894 assert_eq!(term.cursor_row(), 2);
895 assert_eq!(term.cursor_col(), 4);
896 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898 // Scroll back down, removing the blank line at the top.
899 // Cursor stays in place, the text moves under it.
900 term.scroll(1);
901 assert_eq!(term.cursor_row(), 2);
902 assert_eq!(term.cursor_col(), 4);
903 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905 // Clear from here to end-of-line
906 term.clear_eol();
907 assert_eq!(term.cursor_row(), 2);
908 assert_eq!(term.cursor_col(), 4);
909 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911 // Now clear from here to start-of-line. Cursor does not move.
912 term.clear_sol();
913 assert_eq!(term.cursor_row(), 2);
914 assert_eq!(term.cursor_col(), 4);
915 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916 term.cursor_left(1);
917 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918 term.set_cursor_col(0);
919 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921 // Clear some lines
922 term.clear_line(1);
923 assert_eq!(term.cursor_row(), 2);
924 assert_eq!(term.cursor_col(), 0);
925 term.set_cursor_row(1);
926 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927 term.set_cursor_row(3);
928 term.clear_cur_line();
929 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930 assert_eq!(term.cursor_row(), 3);
931 assert_eq!(term.cursor_col(), 0);
932
933 term.append("Two lines above are intentionally left blank.\n");
934 assert_eq!(term.cursor_row(), 4);
935 assert_eq!(term.cursor_col(), 0);
936
937 // Set up the test pattern again, then play with insert/delete
938 term.append("0123456789\n");
939 term.append("ABCDEFGHIJ\n");
940 term.append("abcdefghij\n");
941 assert_eq!(term.cursor_row(), 7);
942
943 term.set_cursor_row(4);
944 term.set_cursor_col(4);
945 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947 term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949 term.cursor_right(5, false);
950 assert_eq!(term.cursor_col(), 9);
951 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953 // Insert two blank rows above cursor. Cursor stays put.
954 term.insert_rows(2);
955 assert_eq!(term.cursor_row(), 4);
956 assert_eq!(term.cursor_col(), 9);
957 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958 term.cursor_down(2, false); // Go down to find our text again
959 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961 // Go back to the beginning of the inserted 'x' characters and delete them.
962 term.cursor_left(5);
963 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964 term.delete_cur_chars(5);
965 assert_eq!(term.cursor_row(), 6);
966 assert_eq!(term.cursor_col(), 4);
967 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969 term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970 term.cursor_down(1, false);
971 term.cursor_left(2);
972 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974 term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975 assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976 term.cursor_up(1, false);
977 term.delete_rows(2); // Delete remains of test pattern
978
979 term.set_text_attrib(Attrib::Bold);
980 term.insert_char_eol('-', 3, 15, 20);
981 term.set_cursor_row(3);
982 term.set_cursor_col(15);
983 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984 assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986 term.set_text_attrib(Attrib::Italic);
987 term.append(" and all lines below");
988 term.set_text_attrib(Attrib::Normal);
989 term.cursor_down(1, false);
990
991 let mut hsb = term.hscrollbar();
992 let mut sb = term.scrollbar();
993 hsb.set_value(100.0);
994 assert_eq!(hsb.value(), 100.0);
995 sb.set_value(50.0);
996 assert_eq!(sb.value(), 50.0);
997 hsb.set_value(0.0);
998 assert_eq!(hsb.value(), 0.0);
999 sb.set_value(0.0);
1000 assert_eq!(sb.value(), 0.0);
1001}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
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?
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700 term.take_focus().unwrap();
701
702 // Test the attr_fg_color and attr_bg_color methods.
703 // Put a single character 'A' into the buffer and check it
704 term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705 term.set_text_bg_color(Color::TransparentBg);
706 term.set_text_fg_color(Color::XtermWhite);
707 term.append("A");
708 let r = &term.u8c_disp_row(0);
709 let uc = r.col(0);
710 assert_eq!(uc.text_utf8(), b"A");
711 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712 assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713 assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714 assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715 assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717 // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718 term.clear();
719 term.set_text_fg_color_xterm(XtermColor::White);
720 term.set_text_bg_color_xterm(XtermColor::Black);
721 assert_eq!(term.text_attrib(), Attrib::Normal);
722
723 assert!(term.ansi());
724 term.append("B\x1b[32mC\x1b[1mD\n");
725
726 let r = &term.u8c_disp_row(0);
727 let uc = r.col(0);
728 assert_eq!(uc.text_utf8(), b"B");
729 assert!(uc.is_char(b'B'));
730 assert!(!uc.is_char(b'A'));
731 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735 assert_eq!(
736 &uc.charflags(),
737 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738 );
739
740 let uc = r.col(1);
741 assert_eq!(uc.text_utf8(), b"C");
742 assert!(uc.is_char(b'C'));
743 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747 assert_eq!(
748 &uc.charflags(),
749 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750 );
751
752 let uc = r.col(2);
753 assert_eq!(uc.text_utf8(), b"D");
754 assert!(uc.is_char(b'D'));
755 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757 assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759 assert_eq!(
760 &uc.attr_fgcolor(Some(term)),
761 &Color::from_rgb(0x20, 0xf0, 0x20)
762 );
763 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764 assert_eq!(
765 &uc.charflags(),
766 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767 );
768
769 // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770 term.clear();
771 term.set_text_fg_color_xterm(XtermColor::White);
772 term.set_text_bg_color_xterm(XtermColor::Black);
773 term.set_text_attrib(Attrib::Normal);
774 assert_eq!(term.text_attrib(), Attrib::Normal);
775
776 assert!(term.ansi());
777 term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779 let r = &term.u8c_disp_row(0);
780 let uc = r.col(0);
781 assert_eq!(uc.text_utf8(), b"B");
782 assert!(uc.is_char(b'B'));
783 assert!(!uc.is_char(b'A'));
784 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788 assert_eq!(
789 &uc.charflags(),
790 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791 );
792
793 let uc = r.col(1);
794 assert_eq!(uc.text_utf8(), b"C");
795 assert!(uc.is_char(b'C'));
796 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800 assert_eq!(
801 &uc.charflags(),
802 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803 );
804
805 let uc = r.col(2);
806 assert_eq!(uc.text_utf8(), b"D");
807 assert!(uc.is_char(b'D'));
808 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812 assert_eq!(
813 &uc.charflags(),
814 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815 );
816
817 let uc = r.col(3);
818 assert_eq!(uc.text_utf8(), b"E");
819 assert!(uc.is_char(b'E'));
820 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822 assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823 assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824 assert_eq!(
825 &uc.charflags(),
826 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827 );
828
829 // Test some miscellaneous Utf8 constants
830 assert_eq!(uc.length(), 1);
831 assert_eq!(uc.max_utf8(), 4);
832 assert_eq!(uc.pwidth(), 9.0);
833 assert_eq!(uc.pwidth_int(), 9);
834
835 term.set_text_fg_color_xterm(XtermColor::White);
836 term.set_text_bg_color_xterm(XtermColor::Black);
837 term.clear();
838 term.set_text_attrib(Attrib::Normal);
839
840 // Mouse selection functions
841 term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842 term.clear_mouse_selection();
843 assert_eq!(term.get_selection(), None);
844
845 // Play with cursor position
846 term.append("0123456789\n"); // Set up test pattern
847 term.append("ABCDEFGHIJ\n");
848 term.append("abcdefghij\n");
849
850 term.set_cursor_row(1);
851 assert_eq!(term.cursor_row(), 1);
852 term.set_cursor_col(1);
853 assert_eq!(term.cursor_col(), 1);
854 assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856 term.append("----"); // Overwrites text at cursor and moves cursor forward
857 assert_eq!(term.cursor_row(), 1);
858 assert_eq!(term.cursor_col(), 5);
859 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860 term.set_cursor_col(1);
861 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863 term.cursor_up(1, false);
864 assert_eq!(term.cursor_row(), 0);
865 assert_eq!(term.cursor_col(), 1);
866 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868 // Hit top of screen, so nothing happens
869 term.cursor_up(1, false);
870 assert_eq!(term.cursor_row(), 0);
871 assert_eq!(term.cursor_col(), 1);
872 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874 // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875 term.cursor_up(1, true);
876 assert_eq!(term.cursor_row(), 0);
877 assert_eq!(term.cursor_col(), 1);
878 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880 // Go back down to the overwritten text
881 term.cursor_down(2, false);
882 assert_eq!(term.cursor_row(), 2);
883 assert_eq!(term.cursor_col(), 1);
884 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886 // Go right past the overwritten text
887 term.cursor_right(4, false);
888 assert_eq!(term.cursor_row(), 2);
889 assert_eq!(term.cursor_col(), 5);
890 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892 // Go left to the end of the overwritten text
893 term.cursor_left(1);
894 assert_eq!(term.cursor_row(), 2);
895 assert_eq!(term.cursor_col(), 4);
896 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898 // Scroll back down, removing the blank line at the top.
899 // Cursor stays in place, the text moves under it.
900 term.scroll(1);
901 assert_eq!(term.cursor_row(), 2);
902 assert_eq!(term.cursor_col(), 4);
903 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905 // Clear from here to end-of-line
906 term.clear_eol();
907 assert_eq!(term.cursor_row(), 2);
908 assert_eq!(term.cursor_col(), 4);
909 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911 // Now clear from here to start-of-line. Cursor does not move.
912 term.clear_sol();
913 assert_eq!(term.cursor_row(), 2);
914 assert_eq!(term.cursor_col(), 4);
915 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916 term.cursor_left(1);
917 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918 term.set_cursor_col(0);
919 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921 // Clear some lines
922 term.clear_line(1);
923 assert_eq!(term.cursor_row(), 2);
924 assert_eq!(term.cursor_col(), 0);
925 term.set_cursor_row(1);
926 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927 term.set_cursor_row(3);
928 term.clear_cur_line();
929 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930 assert_eq!(term.cursor_row(), 3);
931 assert_eq!(term.cursor_col(), 0);
932
933 term.append("Two lines above are intentionally left blank.\n");
934 assert_eq!(term.cursor_row(), 4);
935 assert_eq!(term.cursor_col(), 0);
936
937 // Set up the test pattern again, then play with insert/delete
938 term.append("0123456789\n");
939 term.append("ABCDEFGHIJ\n");
940 term.append("abcdefghij\n");
941 assert_eq!(term.cursor_row(), 7);
942
943 term.set_cursor_row(4);
944 term.set_cursor_col(4);
945 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947 term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949 term.cursor_right(5, false);
950 assert_eq!(term.cursor_col(), 9);
951 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953 // Insert two blank rows above cursor. Cursor stays put.
954 term.insert_rows(2);
955 assert_eq!(term.cursor_row(), 4);
956 assert_eq!(term.cursor_col(), 9);
957 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958 term.cursor_down(2, false); // Go down to find our text again
959 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961 // Go back to the beginning of the inserted 'x' characters and delete them.
962 term.cursor_left(5);
963 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964 term.delete_cur_chars(5);
965 assert_eq!(term.cursor_row(), 6);
966 assert_eq!(term.cursor_col(), 4);
967 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969 term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970 term.cursor_down(1, false);
971 term.cursor_left(2);
972 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974 term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975 assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976 term.cursor_up(1, false);
977 term.delete_rows(2); // Delete remains of test pattern
978
979 term.set_text_attrib(Attrib::Bold);
980 term.insert_char_eol('-', 3, 15, 20);
981 term.set_cursor_row(3);
982 term.set_cursor_col(15);
983 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984 assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986 term.set_text_attrib(Attrib::Italic);
987 term.append(" and all lines below");
988 term.set_text_attrib(Attrib::Normal);
989 term.cursor_down(1, false);
990
991 let mut hsb = term.hscrollbar();
992 let mut sb = term.scrollbar();
993 hsb.set_value(100.0);
994 assert_eq!(hsb.value(), 100.0);
995 sb.set_value(50.0);
996 assert_eq!(sb.value(), 50.0);
997 hsb.set_value(0.0);
998 assert_eq!(hsb.value(), 0.0);
999 sb.set_value(0.0);
1000 assert_eq!(sb.value(), 0.0);
1001}
Sourcepub fn cursor_home(&mut self)
pub fn cursor_home(&mut self)
Move cursor to the home position (top/left).
Examples found in repository?
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496 term.take_focus().unwrap();
497 term.reset_terminal();
498 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500 assert_eq!(term.history_use(), 0);
501 term.clear();
502 assert_eq!(term.cursor_row(), 0);
503 assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505 term.append("Test\ntext\na\nb\nc\nd");
506 assert_eq!(term.cursor_row(), 5);
507 let hist = term.history_use();
508 term.clear_screen_home(false);
509 assert_eq!(term.cursor_row(), 0);
510 assert_eq!(term.history_use(), hist); // History not changed
511
512 term.append("Test\ntext\na\nb\nc\nd\ne");
513 assert_eq!(term.cursor_row(), 6);
514 term.clear_screen_home(true);
515 assert_eq!(term.cursor_row(), 0);
516
517 term.append("Test\ntext\na\nb\nc\n");
518 assert_eq!(term.cursor_row(), 5);
519 term.clear_to_color(Color::DarkBlue);
520 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521 assert_eq!(term.text_bg_color(), Color::TransparentBg);
522 assert_eq!(term.cursor_row(), 0);
523
524 // Test cursor_home()
525 term.append("Test\n\n\n\n\n\n\n\n\n\n");
526 assert_eq!(term.cursor_row(), 10);
527 term.cursor_home();
528 assert_eq!(term.cursor_row(), 0);
529
530 // Test the widget color
531 assert_eq!(term.color(), Color::Black); // Default
532 term.set_color(Color::DarkGreen);
533 assert_eq!(term.color(), Color::DarkGreen);
534 term.set_color(Color::Black);
535 assert_eq!(term.color(), Color::Black);
536 term.append(
537 "This should be one line of white text on black, embedded into the top of a blue field.\n",
538 );
539
540 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541 term.set_output_translate(OutFlags::OFF);
542 assert_eq!(term.output_translate(), OutFlags::OFF);
543 term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547 term.append("\nDone!\n");
548 term.set_text_attrib(Attrib::Normal);
549}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
425//--------------------------------------------------------------------------------------
426/// More tests that run when the menu bar Test1 is clicked
427fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
428 term.take_focus().unwrap();
429 term.reset_terminal();
430 term.append("0123456789 0\n");
431 term.append("0123456789 1\n");
432 term.append("0123456789 2\n");
433 term.append("0123456789 3\n");
434 term.append("0123456789 4\n");
435 term.append("0123456789 5\n");
436 term.append("0123456789 6\n");
437 term.append("0123456789 7\n");
438 term.append("0123456789 8\n");
439 term.append("0123456789 9\n");
440 term.append("------------\n");
441
442 term.set_text_fg_color(Color::Green);
443 term.plot_char('A', 0, 0);
444 term.plot_char('B', 1, 1);
445 term.plot_char('C', 2, 2);
446 term.plot_char('D', 3, 3);
447 term.plot_char('E', 4, 4);
448 term.plot_char('F', 5, 5);
449 term.set_text_fg_color(Color::XtermWhite);
450
451 assert_eq!(term.cursor_row(), 11);
452 assert_eq!(term.cursor_col(), 0);
453
454 term.set_text_bg_color(Color::DarkBlue);
455 term.plot_char_utf8('b', 8, 1);
456 term.plot_char_utf8('↑', 9, 1);
457 term.plot_char_utf8('c', 8, 2);
458 term.plot_char_utf8('↑', 9, 2);
459 term.plot_char_utf8('d', 8, 3);
460 term.plot_char_utf8('↑', 9, 3);
461 term.plot_char_utf8('e', 8, 4);
462 term.plot_char_utf8('↑', 9, 4);
463 term.plot_char_utf8('f', 8, 5);
464 term.plot_char_utf8('↑', 9, 5);
465 term.plot_char_utf8('g', 8, 6);
466 term.plot_char_utf8('↑', 9, 6);
467 term.set_text_bg_color(Color::TransparentBg);
468
469 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
470 term.append("Done!\n");
471 term.set_text_attrib(Attrib::Normal);
472}
473
474//--------------------------------------------------------------------------------------
475/// More tests that run when the menu bar button Test2 is clicked
476fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
477 term.take_focus().unwrap();
478 term.reset_terminal();
479
480 for i in 0..50 {
481 term.append(&format!("{i}\n"));
482 }
483 assert_eq!(term.history_rows(), 100);
484
485 term.clear_history();
486 assert_eq!(term.history_use(), 0);
487
488 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
489 term.append("\nDone!\n");
490 term.set_text_attrib(Attrib::Normal);
491}
492
493//--------------------------------------------------------------------------------------
494/// Another set of tests that run when Test3 is clicked
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496 term.take_focus().unwrap();
497 term.reset_terminal();
498 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500 assert_eq!(term.history_use(), 0);
501 term.clear();
502 assert_eq!(term.cursor_row(), 0);
503 assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505 term.append("Test\ntext\na\nb\nc\nd");
506 assert_eq!(term.cursor_row(), 5);
507 let hist = term.history_use();
508 term.clear_screen_home(false);
509 assert_eq!(term.cursor_row(), 0);
510 assert_eq!(term.history_use(), hist); // History not changed
511
512 term.append("Test\ntext\na\nb\nc\nd\ne");
513 assert_eq!(term.cursor_row(), 6);
514 term.clear_screen_home(true);
515 assert_eq!(term.cursor_row(), 0);
516
517 term.append("Test\ntext\na\nb\nc\n");
518 assert_eq!(term.cursor_row(), 5);
519 term.clear_to_color(Color::DarkBlue);
520 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521 assert_eq!(term.text_bg_color(), Color::TransparentBg);
522 assert_eq!(term.cursor_row(), 0);
523
524 // Test cursor_home()
525 term.append("Test\n\n\n\n\n\n\n\n\n\n");
526 assert_eq!(term.cursor_row(), 10);
527 term.cursor_home();
528 assert_eq!(term.cursor_row(), 0);
529
530 // Test the widget color
531 assert_eq!(term.color(), Color::Black); // Default
532 term.set_color(Color::DarkGreen);
533 assert_eq!(term.color(), Color::DarkGreen);
534 term.set_color(Color::Black);
535 assert_eq!(term.color(), Color::Black);
536 term.append(
537 "This should be one line of white text on black, embedded into the top of a blue field.\n",
538 );
539
540 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541 term.set_output_translate(OutFlags::OFF);
542 assert_eq!(term.output_translate(), OutFlags::OFF);
543 term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547 term.append("\nDone!\n");
548 term.set_text_attrib(Attrib::Normal);
549}
550
551//--------------------------------------------------------------------------------------
552/// Another set of tests for the ring-buffer access methods
553/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555 let sel_len = term.selection_text_len();
556 let sel = term.selection_text();
557
558 term.take_focus().unwrap();
559 term.reset_terminal();
560 // Test the Utf8Char primitive
561 let uc = Utf8Char::new(b'Q');
562 let uc1 = uc.text_utf8();
563 assert_eq!(&uc1, b"Q");
564 assert_eq!(&uc.attrib(), &Attrib::Normal);
565 assert_eq!(
566 &uc.charflags(),
567 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568 );
569 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570 assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572 let ring_rows = term.ring_rows();
573
574 term.take_focus().unwrap();
575 term.clear_history();
576 assert_eq!(term.history_use(), 0);
577
578 // Subtract row numbers, modulo `rows`
579 fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580 match a - b {
581 n if n < 0 => n + rows,
582 n => n,
583 }
584 }
585 // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587 assert!(term.disp_srow() >= 0);
588 assert!(term.disp_erow() >= 0);
589 assert!(term.hist_srow() >= 0);
590 assert!(term.hist_erow() >= 0);
591 assert!(term.offset() >= 0);
592 assert!(term.disp_srow() <= ring_rows);
593 assert!(term.disp_erow() <= ring_rows);
594 assert!(term.hist_srow() <= ring_rows);
595 assert!(term.hist_erow() <= ring_rows);
596 assert!(term.offset() <= ring_rows);
597
598 assert_eq!(term.ring_srow(), 0);
599 assert_eq!(term.ring_erow(), ring_rows - 1);
600 assert_eq!(
601 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602 term.display_rows()
603 );
604 assert_eq!(
605 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606 term.history_rows()
607 );
608
609 assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610 assert_eq!(term.ring_srow(), 0);
611
612 /// Local function to read back all rows from the display into a long string.
613 /// Does not include scrollback history.
614 /// Trims trailing blanks on each line
615 fn read_disp(term: &Terminal) -> String {
616 let rows = term.display_rows();
617 let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618 for row in 0..rows {
619 let r = term.u8c_disp_row(row).trim();
620 // Iterate through a row, accumulating [u8]
621 for c in r.iter() {
622 // Note: Sometimes utf-8 length is > 1
623 text.extend_from_slice(c.text_utf8());
624 }
625 text.extend_from_slice(b"\n");
626 }
627 // Return the result as a string
628 std::str::from_utf8(&text).unwrap().to_string()
629 }
630
631 term.clear();
632 term.append("Top line ↑ (up-arrow)");
633 term.set_text_attrib(Attrib::Underline);
634 term.append(" ");
635 term.set_text_attrib(Attrib::Normal);
636 term.append(" \n");
637 let mut text_out = read_disp(term);
638 // Trim trailing empty lines
639 text_out = text_out.trim_end_matches(&"\n").to_string();
640 // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642 assert_eq!(text_out, "Top line ↑ (up-arrow) ");
643 let r = term.u8c_disp_row(0);
644 assert_eq!(r.col(0).text_utf8(), b"T");
645 assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646 assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647 let r = term.u8c_disp_row(1);
648 assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649 assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651 // Clear the screen again, then append test text, then read it back and compare
652 let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656 Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659 term.clear_history();
660 term.clear();
661 let bg_save = term.text_bg_color();
662 let fg_save = term.text_fg_color();
663 term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664 term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665 term.append(test_text);
666 term.set_text_bg_color(bg_save);
667 term.set_text_fg_color(fg_save);
668
669 let mut text_out = read_disp(term);
670 // Trim trailing empty lines
671 text_out = text_out.trim_end_matches(&"\n").to_string();
672 assert_eq!(test_text, text_out);
673
674 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676 assert_eq!(term.ring_srow(), 0);
677 assert_eq!(term.ring_erow(), ring_rows - 1);
678 assert_eq!(
679 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680 term.display_rows()
681 );
682 assert_eq!(
683 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684 term.history_rows()
685 );
686
687 term.append(&format!(
688 "\n\nScreen has {} rows of {} columns.\n",
689 term.display_rows(),
690 term.display_columns()
691 ));
692
693 term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
425//--------------------------------------------------------------------------------------
426/// More tests that run when the menu bar Test1 is clicked
427fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
428 term.take_focus().unwrap();
429 term.reset_terminal();
430 term.append("0123456789 0\n");
431 term.append("0123456789 1\n");
432 term.append("0123456789 2\n");
433 term.append("0123456789 3\n");
434 term.append("0123456789 4\n");
435 term.append("0123456789 5\n");
436 term.append("0123456789 6\n");
437 term.append("0123456789 7\n");
438 term.append("0123456789 8\n");
439 term.append("0123456789 9\n");
440 term.append("------------\n");
441
442 term.set_text_fg_color(Color::Green);
443 term.plot_char('A', 0, 0);
444 term.plot_char('B', 1, 1);
445 term.plot_char('C', 2, 2);
446 term.plot_char('D', 3, 3);
447 term.plot_char('E', 4, 4);
448 term.plot_char('F', 5, 5);
449 term.set_text_fg_color(Color::XtermWhite);
450
451 assert_eq!(term.cursor_row(), 11);
452 assert_eq!(term.cursor_col(), 0);
453
454 term.set_text_bg_color(Color::DarkBlue);
455 term.plot_char_utf8('b', 8, 1);
456 term.plot_char_utf8('↑', 9, 1);
457 term.plot_char_utf8('c', 8, 2);
458 term.plot_char_utf8('↑', 9, 2);
459 term.plot_char_utf8('d', 8, 3);
460 term.plot_char_utf8('↑', 9, 3);
461 term.plot_char_utf8('e', 8, 4);
462 term.plot_char_utf8('↑', 9, 4);
463 term.plot_char_utf8('f', 8, 5);
464 term.plot_char_utf8('↑', 9, 5);
465 term.plot_char_utf8('g', 8, 6);
466 term.plot_char_utf8('↑', 9, 6);
467 term.set_text_bg_color(Color::TransparentBg);
468
469 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
470 term.append("Done!\n");
471 term.set_text_attrib(Attrib::Normal);
472}
473
474//--------------------------------------------------------------------------------------
475/// More tests that run when the menu bar button Test2 is clicked
476fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
477 term.take_focus().unwrap();
478 term.reset_terminal();
479
480 for i in 0..50 {
481 term.append(&format!("{i}\n"));
482 }
483 assert_eq!(term.history_rows(), 100);
484
485 term.clear_history();
486 assert_eq!(term.history_use(), 0);
487
488 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
489 term.append("\nDone!\n");
490 term.set_text_attrib(Attrib::Normal);
491}
492
493//--------------------------------------------------------------------------------------
494/// Another set of tests that run when Test3 is clicked
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496 term.take_focus().unwrap();
497 term.reset_terminal();
498 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500 assert_eq!(term.history_use(), 0);
501 term.clear();
502 assert_eq!(term.cursor_row(), 0);
503 assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505 term.append("Test\ntext\na\nb\nc\nd");
506 assert_eq!(term.cursor_row(), 5);
507 let hist = term.history_use();
508 term.clear_screen_home(false);
509 assert_eq!(term.cursor_row(), 0);
510 assert_eq!(term.history_use(), hist); // History not changed
511
512 term.append("Test\ntext\na\nb\nc\nd\ne");
513 assert_eq!(term.cursor_row(), 6);
514 term.clear_screen_home(true);
515 assert_eq!(term.cursor_row(), 0);
516
517 term.append("Test\ntext\na\nb\nc\n");
518 assert_eq!(term.cursor_row(), 5);
519 term.clear_to_color(Color::DarkBlue);
520 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521 assert_eq!(term.text_bg_color(), Color::TransparentBg);
522 assert_eq!(term.cursor_row(), 0);
523
524 // Test cursor_home()
525 term.append("Test\n\n\n\n\n\n\n\n\n\n");
526 assert_eq!(term.cursor_row(), 10);
527 term.cursor_home();
528 assert_eq!(term.cursor_row(), 0);
529
530 // Test the widget color
531 assert_eq!(term.color(), Color::Black); // Default
532 term.set_color(Color::DarkGreen);
533 assert_eq!(term.color(), Color::DarkGreen);
534 term.set_color(Color::Black);
535 assert_eq!(term.color(), Color::Black);
536 term.append(
537 "This should be one line of white text on black, embedded into the top of a blue field.\n",
538 );
539
540 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541 term.set_output_translate(OutFlags::OFF);
542 assert_eq!(term.output_translate(), OutFlags::OFF);
543 term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547 term.append("\nDone!\n");
548 term.set_text_attrib(Attrib::Normal);
549}
550
551//--------------------------------------------------------------------------------------
552/// Another set of tests for the ring-buffer access methods
553/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555 let sel_len = term.selection_text_len();
556 let sel = term.selection_text();
557
558 term.take_focus().unwrap();
559 term.reset_terminal();
560 // Test the Utf8Char primitive
561 let uc = Utf8Char::new(b'Q');
562 let uc1 = uc.text_utf8();
563 assert_eq!(&uc1, b"Q");
564 assert_eq!(&uc.attrib(), &Attrib::Normal);
565 assert_eq!(
566 &uc.charflags(),
567 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568 );
569 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570 assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572 let ring_rows = term.ring_rows();
573
574 term.take_focus().unwrap();
575 term.clear_history();
576 assert_eq!(term.history_use(), 0);
577
578 // Subtract row numbers, modulo `rows`
579 fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580 match a - b {
581 n if n < 0 => n + rows,
582 n => n,
583 }
584 }
585 // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587 assert!(term.disp_srow() >= 0);
588 assert!(term.disp_erow() >= 0);
589 assert!(term.hist_srow() >= 0);
590 assert!(term.hist_erow() >= 0);
591 assert!(term.offset() >= 0);
592 assert!(term.disp_srow() <= ring_rows);
593 assert!(term.disp_erow() <= ring_rows);
594 assert!(term.hist_srow() <= ring_rows);
595 assert!(term.hist_erow() <= ring_rows);
596 assert!(term.offset() <= ring_rows);
597
598 assert_eq!(term.ring_srow(), 0);
599 assert_eq!(term.ring_erow(), ring_rows - 1);
600 assert_eq!(
601 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602 term.display_rows()
603 );
604 assert_eq!(
605 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606 term.history_rows()
607 );
608
609 assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610 assert_eq!(term.ring_srow(), 0);
611
612 /// Local function to read back all rows from the display into a long string.
613 /// Does not include scrollback history.
614 /// Trims trailing blanks on each line
615 fn read_disp(term: &Terminal) -> String {
616 let rows = term.display_rows();
617 let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618 for row in 0..rows {
619 let r = term.u8c_disp_row(row).trim();
620 // Iterate through a row, accumulating [u8]
621 for c in r.iter() {
622 // Note: Sometimes utf-8 length is > 1
623 text.extend_from_slice(c.text_utf8());
624 }
625 text.extend_from_slice(b"\n");
626 }
627 // Return the result as a string
628 std::str::from_utf8(&text).unwrap().to_string()
629 }
630
631 term.clear();
632 term.append("Top line ↑ (up-arrow)");
633 term.set_text_attrib(Attrib::Underline);
634 term.append(" ");
635 term.set_text_attrib(Attrib::Normal);
636 term.append(" \n");
637 let mut text_out = read_disp(term);
638 // Trim trailing empty lines
639 text_out = text_out.trim_end_matches(&"\n").to_string();
640 // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642 assert_eq!(text_out, "Top line ↑ (up-arrow) ");
643 let r = term.u8c_disp_row(0);
644 assert_eq!(r.col(0).text_utf8(), b"T");
645 assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646 assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647 let r = term.u8c_disp_row(1);
648 assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649 assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651 // Clear the screen again, then append test text, then read it back and compare
652 let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656 Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659 term.clear_history();
660 term.clear();
661 let bg_save = term.text_bg_color();
662 let fg_save = term.text_fg_color();
663 term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664 term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665 term.append(test_text);
666 term.set_text_bg_color(bg_save);
667 term.set_text_fg_color(fg_save);
668
669 let mut text_out = read_disp(term);
670 // Trim trailing empty lines
671 text_out = text_out.trim_end_matches(&"\n").to_string();
672 assert_eq!(test_text, text_out);
673
674 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676 assert_eq!(term.ring_srow(), 0);
677 assert_eq!(term.ring_erow(), ring_rows - 1);
678 assert_eq!(
679 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680 term.display_rows()
681 );
682 assert_eq!(
683 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684 term.history_rows()
685 );
686
687 term.append(&format!(
688 "\n\nScreen has {} rows of {} columns.\n",
689 term.display_rows(),
690 term.display_columns()
691 ));
692
693 term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
425//--------------------------------------------------------------------------------------
426/// More tests that run when the menu bar Test1 is clicked
427fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
428 term.take_focus().unwrap();
429 term.reset_terminal();
430 term.append("0123456789 0\n");
431 term.append("0123456789 1\n");
432 term.append("0123456789 2\n");
433 term.append("0123456789 3\n");
434 term.append("0123456789 4\n");
435 term.append("0123456789 5\n");
436 term.append("0123456789 6\n");
437 term.append("0123456789 7\n");
438 term.append("0123456789 8\n");
439 term.append("0123456789 9\n");
440 term.append("------------\n");
441
442 term.set_text_fg_color(Color::Green);
443 term.plot_char('A', 0, 0);
444 term.plot_char('B', 1, 1);
445 term.plot_char('C', 2, 2);
446 term.plot_char('D', 3, 3);
447 term.plot_char('E', 4, 4);
448 term.plot_char('F', 5, 5);
449 term.set_text_fg_color(Color::XtermWhite);
450
451 assert_eq!(term.cursor_row(), 11);
452 assert_eq!(term.cursor_col(), 0);
453
454 term.set_text_bg_color(Color::DarkBlue);
455 term.plot_char_utf8('b', 8, 1);
456 term.plot_char_utf8('↑', 9, 1);
457 term.plot_char_utf8('c', 8, 2);
458 term.plot_char_utf8('↑', 9, 2);
459 term.plot_char_utf8('d', 8, 3);
460 term.plot_char_utf8('↑', 9, 3);
461 term.plot_char_utf8('e', 8, 4);
462 term.plot_char_utf8('↑', 9, 4);
463 term.plot_char_utf8('f', 8, 5);
464 term.plot_char_utf8('↑', 9, 5);
465 term.plot_char_utf8('g', 8, 6);
466 term.plot_char_utf8('↑', 9, 6);
467 term.set_text_bg_color(Color::TransparentBg);
468
469 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
470 term.append("Done!\n");
471 term.set_text_attrib(Attrib::Normal);
472}
473
474//--------------------------------------------------------------------------------------
475/// More tests that run when the menu bar button Test2 is clicked
476fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
477 term.take_focus().unwrap();
478 term.reset_terminal();
479
480 for i in 0..50 {
481 term.append(&format!("{i}\n"));
482 }
483 assert_eq!(term.history_rows(), 100);
484
485 term.clear_history();
486 assert_eq!(term.history_use(), 0);
487
488 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
489 term.append("\nDone!\n");
490 term.set_text_attrib(Attrib::Normal);
491}
492
493//--------------------------------------------------------------------------------------
494/// Another set of tests that run when Test3 is clicked
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496 term.take_focus().unwrap();
497 term.reset_terminal();
498 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500 assert_eq!(term.history_use(), 0);
501 term.clear();
502 assert_eq!(term.cursor_row(), 0);
503 assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505 term.append("Test\ntext\na\nb\nc\nd");
506 assert_eq!(term.cursor_row(), 5);
507 let hist = term.history_use();
508 term.clear_screen_home(false);
509 assert_eq!(term.cursor_row(), 0);
510 assert_eq!(term.history_use(), hist); // History not changed
511
512 term.append("Test\ntext\na\nb\nc\nd\ne");
513 assert_eq!(term.cursor_row(), 6);
514 term.clear_screen_home(true);
515 assert_eq!(term.cursor_row(), 0);
516
517 term.append("Test\ntext\na\nb\nc\n");
518 assert_eq!(term.cursor_row(), 5);
519 term.clear_to_color(Color::DarkBlue);
520 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521 assert_eq!(term.text_bg_color(), Color::TransparentBg);
522 assert_eq!(term.cursor_row(), 0);
523
524 // Test cursor_home()
525 term.append("Test\n\n\n\n\n\n\n\n\n\n");
526 assert_eq!(term.cursor_row(), 10);
527 term.cursor_home();
528 assert_eq!(term.cursor_row(), 0);
529
530 // Test the widget color
531 assert_eq!(term.color(), Color::Black); // Default
532 term.set_color(Color::DarkGreen);
533 assert_eq!(term.color(), Color::DarkGreen);
534 term.set_color(Color::Black);
535 assert_eq!(term.color(), Color::Black);
536 term.append(
537 "This should be one line of white text on black, embedded into the top of a blue field.\n",
538 );
539
540 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541 term.set_output_translate(OutFlags::OFF);
542 assert_eq!(term.output_translate(), OutFlags::OFF);
543 term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547 term.append("\nDone!\n");
548 term.set_text_attrib(Attrib::Normal);
549}
550
551//--------------------------------------------------------------------------------------
552/// Another set of tests for the ring-buffer access methods
553/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555 let sel_len = term.selection_text_len();
556 let sel = term.selection_text();
557
558 term.take_focus().unwrap();
559 term.reset_terminal();
560 // Test the Utf8Char primitive
561 let uc = Utf8Char::new(b'Q');
562 let uc1 = uc.text_utf8();
563 assert_eq!(&uc1, b"Q");
564 assert_eq!(&uc.attrib(), &Attrib::Normal);
565 assert_eq!(
566 &uc.charflags(),
567 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568 );
569 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570 assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572 let ring_rows = term.ring_rows();
573
574 term.take_focus().unwrap();
575 term.clear_history();
576 assert_eq!(term.history_use(), 0);
577
578 // Subtract row numbers, modulo `rows`
579 fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580 match a - b {
581 n if n < 0 => n + rows,
582 n => n,
583 }
584 }
585 // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587 assert!(term.disp_srow() >= 0);
588 assert!(term.disp_erow() >= 0);
589 assert!(term.hist_srow() >= 0);
590 assert!(term.hist_erow() >= 0);
591 assert!(term.offset() >= 0);
592 assert!(term.disp_srow() <= ring_rows);
593 assert!(term.disp_erow() <= ring_rows);
594 assert!(term.hist_srow() <= ring_rows);
595 assert!(term.hist_erow() <= ring_rows);
596 assert!(term.offset() <= ring_rows);
597
598 assert_eq!(term.ring_srow(), 0);
599 assert_eq!(term.ring_erow(), ring_rows - 1);
600 assert_eq!(
601 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602 term.display_rows()
603 );
604 assert_eq!(
605 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606 term.history_rows()
607 );
608
609 assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610 assert_eq!(term.ring_srow(), 0);
611
612 /// Local function to read back all rows from the display into a long string.
613 /// Does not include scrollback history.
614 /// Trims trailing blanks on each line
615 fn read_disp(term: &Terminal) -> String {
616 let rows = term.display_rows();
617 let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618 for row in 0..rows {
619 let r = term.u8c_disp_row(row).trim();
620 // Iterate through a row, accumulating [u8]
621 for c in r.iter() {
622 // Note: Sometimes utf-8 length is > 1
623 text.extend_from_slice(c.text_utf8());
624 }
625 text.extend_from_slice(b"\n");
626 }
627 // Return the result as a string
628 std::str::from_utf8(&text).unwrap().to_string()
629 }
630
631 term.clear();
632 term.append("Top line ↑ (up-arrow)");
633 term.set_text_attrib(Attrib::Underline);
634 term.append(" ");
635 term.set_text_attrib(Attrib::Normal);
636 term.append(" \n");
637 let mut text_out = read_disp(term);
638 // Trim trailing empty lines
639 text_out = text_out.trim_end_matches(&"\n").to_string();
640 // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642 assert_eq!(text_out, "Top line ↑ (up-arrow) ");
643 let r = term.u8c_disp_row(0);
644 assert_eq!(r.col(0).text_utf8(), b"T");
645 assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646 assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647 let r = term.u8c_disp_row(1);
648 assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649 assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651 // Clear the screen again, then append test text, then read it back and compare
652 let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656 Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659 term.clear_history();
660 term.clear();
661 let bg_save = term.text_bg_color();
662 let fg_save = term.text_fg_color();
663 term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664 term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665 term.append(test_text);
666 term.set_text_bg_color(bg_save);
667 term.set_text_fg_color(fg_save);
668
669 let mut text_out = read_disp(term);
670 // Trim trailing empty lines
671 text_out = text_out.trim_end_matches(&"\n").to_string();
672 assert_eq!(test_text, text_out);
673
674 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676 assert_eq!(term.ring_srow(), 0);
677 assert_eq!(term.ring_erow(), ring_rows - 1);
678 assert_eq!(
679 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680 term.display_rows()
681 );
682 assert_eq!(
683 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684 term.history_rows()
685 );
686
687 term.append(&format!(
688 "\n\nScreen has {} rows of {} columns.\n",
689 term.display_rows(),
690 term.display_columns()
691 ));
692
693 term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
425//--------------------------------------------------------------------------------------
426/// More tests that run when the menu bar Test1 is clicked
427fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
428 term.take_focus().unwrap();
429 term.reset_terminal();
430 term.append("0123456789 0\n");
431 term.append("0123456789 1\n");
432 term.append("0123456789 2\n");
433 term.append("0123456789 3\n");
434 term.append("0123456789 4\n");
435 term.append("0123456789 5\n");
436 term.append("0123456789 6\n");
437 term.append("0123456789 7\n");
438 term.append("0123456789 8\n");
439 term.append("0123456789 9\n");
440 term.append("------------\n");
441
442 term.set_text_fg_color(Color::Green);
443 term.plot_char('A', 0, 0);
444 term.plot_char('B', 1, 1);
445 term.plot_char('C', 2, 2);
446 term.plot_char('D', 3, 3);
447 term.plot_char('E', 4, 4);
448 term.plot_char('F', 5, 5);
449 term.set_text_fg_color(Color::XtermWhite);
450
451 assert_eq!(term.cursor_row(), 11);
452 assert_eq!(term.cursor_col(), 0);
453
454 term.set_text_bg_color(Color::DarkBlue);
455 term.plot_char_utf8('b', 8, 1);
456 term.plot_char_utf8('↑', 9, 1);
457 term.plot_char_utf8('c', 8, 2);
458 term.plot_char_utf8('↑', 9, 2);
459 term.plot_char_utf8('d', 8, 3);
460 term.plot_char_utf8('↑', 9, 3);
461 term.plot_char_utf8('e', 8, 4);
462 term.plot_char_utf8('↑', 9, 4);
463 term.plot_char_utf8('f', 8, 5);
464 term.plot_char_utf8('↑', 9, 5);
465 term.plot_char_utf8('g', 8, 6);
466 term.plot_char_utf8('↑', 9, 6);
467 term.set_text_bg_color(Color::TransparentBg);
468
469 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
470 term.append("Done!\n");
471 term.set_text_attrib(Attrib::Normal);
472}
473
474//--------------------------------------------------------------------------------------
475/// More tests that run when the menu bar button Test2 is clicked
476fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
477 term.take_focus().unwrap();
478 term.reset_terminal();
479
480 for i in 0..50 {
481 term.append(&format!("{i}\n"));
482 }
483 assert_eq!(term.history_rows(), 100);
484
485 term.clear_history();
486 assert_eq!(term.history_use(), 0);
487
488 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
489 term.append("\nDone!\n");
490 term.set_text_attrib(Attrib::Normal);
491}
492
493//--------------------------------------------------------------------------------------
494/// Another set of tests that run when Test3 is clicked
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496 term.take_focus().unwrap();
497 term.reset_terminal();
498 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500 assert_eq!(term.history_use(), 0);
501 term.clear();
502 assert_eq!(term.cursor_row(), 0);
503 assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505 term.append("Test\ntext\na\nb\nc\nd");
506 assert_eq!(term.cursor_row(), 5);
507 let hist = term.history_use();
508 term.clear_screen_home(false);
509 assert_eq!(term.cursor_row(), 0);
510 assert_eq!(term.history_use(), hist); // History not changed
511
512 term.append("Test\ntext\na\nb\nc\nd\ne");
513 assert_eq!(term.cursor_row(), 6);
514 term.clear_screen_home(true);
515 assert_eq!(term.cursor_row(), 0);
516
517 term.append("Test\ntext\na\nb\nc\n");
518 assert_eq!(term.cursor_row(), 5);
519 term.clear_to_color(Color::DarkBlue);
520 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521 assert_eq!(term.text_bg_color(), Color::TransparentBg);
522 assert_eq!(term.cursor_row(), 0);
523
524 // Test cursor_home()
525 term.append("Test\n\n\n\n\n\n\n\n\n\n");
526 assert_eq!(term.cursor_row(), 10);
527 term.cursor_home();
528 assert_eq!(term.cursor_row(), 0);
529
530 // Test the widget color
531 assert_eq!(term.color(), Color::Black); // Default
532 term.set_color(Color::DarkGreen);
533 assert_eq!(term.color(), Color::DarkGreen);
534 term.set_color(Color::Black);
535 assert_eq!(term.color(), Color::Black);
536 term.append(
537 "This should be one line of white text on black, embedded into the top of a blue field.\n",
538 );
539
540 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541 term.set_output_translate(OutFlags::OFF);
542 assert_eq!(term.output_translate(), OutFlags::OFF);
543 term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547 term.append("\nDone!\n");
548 term.set_text_attrib(Attrib::Normal);
549}
550
551//--------------------------------------------------------------------------------------
552/// Another set of tests for the ring-buffer access methods
553/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555 let sel_len = term.selection_text_len();
556 let sel = term.selection_text();
557
558 term.take_focus().unwrap();
559 term.reset_terminal();
560 // Test the Utf8Char primitive
561 let uc = Utf8Char::new(b'Q');
562 let uc1 = uc.text_utf8();
563 assert_eq!(&uc1, b"Q");
564 assert_eq!(&uc.attrib(), &Attrib::Normal);
565 assert_eq!(
566 &uc.charflags(),
567 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568 );
569 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570 assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572 let ring_rows = term.ring_rows();
573
574 term.take_focus().unwrap();
575 term.clear_history();
576 assert_eq!(term.history_use(), 0);
577
578 // Subtract row numbers, modulo `rows`
579 fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580 match a - b {
581 n if n < 0 => n + rows,
582 n => n,
583 }
584 }
585 // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587 assert!(term.disp_srow() >= 0);
588 assert!(term.disp_erow() >= 0);
589 assert!(term.hist_srow() >= 0);
590 assert!(term.hist_erow() >= 0);
591 assert!(term.offset() >= 0);
592 assert!(term.disp_srow() <= ring_rows);
593 assert!(term.disp_erow() <= ring_rows);
594 assert!(term.hist_srow() <= ring_rows);
595 assert!(term.hist_erow() <= ring_rows);
596 assert!(term.offset() <= ring_rows);
597
598 assert_eq!(term.ring_srow(), 0);
599 assert_eq!(term.ring_erow(), ring_rows - 1);
600 assert_eq!(
601 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602 term.display_rows()
603 );
604 assert_eq!(
605 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606 term.history_rows()
607 );
608
609 assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610 assert_eq!(term.ring_srow(), 0);
611
612 /// Local function to read back all rows from the display into a long string.
613 /// Does not include scrollback history.
614 /// Trims trailing blanks on each line
615 fn read_disp(term: &Terminal) -> String {
616 let rows = term.display_rows();
617 let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618 for row in 0..rows {
619 let r = term.u8c_disp_row(row).trim();
620 // Iterate through a row, accumulating [u8]
621 for c in r.iter() {
622 // Note: Sometimes utf-8 length is > 1
623 text.extend_from_slice(c.text_utf8());
624 }
625 text.extend_from_slice(b"\n");
626 }
627 // Return the result as a string
628 std::str::from_utf8(&text).unwrap().to_string()
629 }
630
631 term.clear();
632 term.append("Top line ↑ (up-arrow)");
633 term.set_text_attrib(Attrib::Underline);
634 term.append(" ");
635 term.set_text_attrib(Attrib::Normal);
636 term.append(" \n");
637 let mut text_out = read_disp(term);
638 // Trim trailing empty lines
639 text_out = text_out.trim_end_matches(&"\n").to_string();
640 // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642 assert_eq!(text_out, "Top line ↑ (up-arrow) ");
643 let r = term.u8c_disp_row(0);
644 assert_eq!(r.col(0).text_utf8(), b"T");
645 assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646 assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647 let r = term.u8c_disp_row(1);
648 assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649 assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651 // Clear the screen again, then append test text, then read it back and compare
652 let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656 Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659 term.clear_history();
660 term.clear();
661 let bg_save = term.text_bg_color();
662 let fg_save = term.text_fg_color();
663 term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664 term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665 term.append(test_text);
666 term.set_text_bg_color(bg_save);
667 term.set_text_fg_color(fg_save);
668
669 let mut text_out = read_disp(term);
670 // Trim trailing empty lines
671 text_out = text_out.trim_end_matches(&"\n").to_string();
672 assert_eq!(test_text, text_out);
673
674 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676 assert_eq!(term.ring_srow(), 0);
677 assert_eq!(term.ring_erow(), ring_rows - 1);
678 assert_eq!(
679 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680 term.display_rows()
681 );
682 assert_eq!(
683 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684 term.history_rows()
685 );
686
687 term.append(&format!(
688 "\n\nScreen has {} rows of {} columns.\n",
689 term.display_rows(),
690 term.display_columns()
691 ));
692
693 term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
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?
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496 term.take_focus().unwrap();
497 term.reset_terminal();
498 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500 assert_eq!(term.history_use(), 0);
501 term.clear();
502 assert_eq!(term.cursor_row(), 0);
503 assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505 term.append("Test\ntext\na\nb\nc\nd");
506 assert_eq!(term.cursor_row(), 5);
507 let hist = term.history_use();
508 term.clear_screen_home(false);
509 assert_eq!(term.cursor_row(), 0);
510 assert_eq!(term.history_use(), hist); // History not changed
511
512 term.append("Test\ntext\na\nb\nc\nd\ne");
513 assert_eq!(term.cursor_row(), 6);
514 term.clear_screen_home(true);
515 assert_eq!(term.cursor_row(), 0);
516
517 term.append("Test\ntext\na\nb\nc\n");
518 assert_eq!(term.cursor_row(), 5);
519 term.clear_to_color(Color::DarkBlue);
520 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521 assert_eq!(term.text_bg_color(), Color::TransparentBg);
522 assert_eq!(term.cursor_row(), 0);
523
524 // Test cursor_home()
525 term.append("Test\n\n\n\n\n\n\n\n\n\n");
526 assert_eq!(term.cursor_row(), 10);
527 term.cursor_home();
528 assert_eq!(term.cursor_row(), 0);
529
530 // Test the widget color
531 assert_eq!(term.color(), Color::Black); // Default
532 term.set_color(Color::DarkGreen);
533 assert_eq!(term.color(), Color::DarkGreen);
534 term.set_color(Color::Black);
535 assert_eq!(term.color(), Color::Black);
536 term.append(
537 "This should be one line of white text on black, embedded into the top of a blue field.\n",
538 );
539
540 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541 term.set_output_translate(OutFlags::OFF);
542 assert_eq!(term.output_translate(), OutFlags::OFF);
543 term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547 term.append("\nDone!\n");
548 term.set_text_attrib(Attrib::Normal);
549}
Sourcepub fn output_translate(&self) -> OutFlags
pub fn output_translate(&self) -> OutFlags
Return the current combined output translation flags.
Examples found in repository?
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496 term.take_focus().unwrap();
497 term.reset_terminal();
498 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500 assert_eq!(term.history_use(), 0);
501 term.clear();
502 assert_eq!(term.cursor_row(), 0);
503 assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505 term.append("Test\ntext\na\nb\nc\nd");
506 assert_eq!(term.cursor_row(), 5);
507 let hist = term.history_use();
508 term.clear_screen_home(false);
509 assert_eq!(term.cursor_row(), 0);
510 assert_eq!(term.history_use(), hist); // History not changed
511
512 term.append("Test\ntext\na\nb\nc\nd\ne");
513 assert_eq!(term.cursor_row(), 6);
514 term.clear_screen_home(true);
515 assert_eq!(term.cursor_row(), 0);
516
517 term.append("Test\ntext\na\nb\nc\n");
518 assert_eq!(term.cursor_row(), 5);
519 term.clear_to_color(Color::DarkBlue);
520 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521 assert_eq!(term.text_bg_color(), Color::TransparentBg);
522 assert_eq!(term.cursor_row(), 0);
523
524 // Test cursor_home()
525 term.append("Test\n\n\n\n\n\n\n\n\n\n");
526 assert_eq!(term.cursor_row(), 10);
527 term.cursor_home();
528 assert_eq!(term.cursor_row(), 0);
529
530 // Test the widget color
531 assert_eq!(term.color(), Color::Black); // Default
532 term.set_color(Color::DarkGreen);
533 assert_eq!(term.color(), Color::DarkGreen);
534 term.set_color(Color::Black);
535 assert_eq!(term.color(), Color::Black);
536 term.append(
537 "This should be one line of white text on black, embedded into the top of a blue field.\n",
538 );
539
540 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541 term.set_output_translate(OutFlags::OFF);
542 assert_eq!(term.output_translate(), OutFlags::OFF);
543 term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547 term.append("\nDone!\n");
548 term.set_text_attrib(Attrib::Normal);
549}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
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 asprint_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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
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?
427fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
428 term.take_focus().unwrap();
429 term.reset_terminal();
430 term.append("0123456789 0\n");
431 term.append("0123456789 1\n");
432 term.append("0123456789 2\n");
433 term.append("0123456789 3\n");
434 term.append("0123456789 4\n");
435 term.append("0123456789 5\n");
436 term.append("0123456789 6\n");
437 term.append("0123456789 7\n");
438 term.append("0123456789 8\n");
439 term.append("0123456789 9\n");
440 term.append("------------\n");
441
442 term.set_text_fg_color(Color::Green);
443 term.plot_char('A', 0, 0);
444 term.plot_char('B', 1, 1);
445 term.plot_char('C', 2, 2);
446 term.plot_char('D', 3, 3);
447 term.plot_char('E', 4, 4);
448 term.plot_char('F', 5, 5);
449 term.set_text_fg_color(Color::XtermWhite);
450
451 assert_eq!(term.cursor_row(), 11);
452 assert_eq!(term.cursor_col(), 0);
453
454 term.set_text_bg_color(Color::DarkBlue);
455 term.plot_char_utf8('b', 8, 1);
456 term.plot_char_utf8('↑', 9, 1);
457 term.plot_char_utf8('c', 8, 2);
458 term.plot_char_utf8('↑', 9, 2);
459 term.plot_char_utf8('d', 8, 3);
460 term.plot_char_utf8('↑', 9, 3);
461 term.plot_char_utf8('e', 8, 4);
462 term.plot_char_utf8('↑', 9, 4);
463 term.plot_char_utf8('f', 8, 5);
464 term.plot_char_utf8('↑', 9, 5);
465 term.plot_char_utf8('g', 8, 6);
466 term.plot_char_utf8('↑', 9, 6);
467 term.set_text_bg_color(Color::TransparentBg);
468
469 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
470 term.append("Done!\n");
471 term.set_text_attrib(Attrib::Normal);
472}
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?
427fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
428 term.take_focus().unwrap();
429 term.reset_terminal();
430 term.append("0123456789 0\n");
431 term.append("0123456789 1\n");
432 term.append("0123456789 2\n");
433 term.append("0123456789 3\n");
434 term.append("0123456789 4\n");
435 term.append("0123456789 5\n");
436 term.append("0123456789 6\n");
437 term.append("0123456789 7\n");
438 term.append("0123456789 8\n");
439 term.append("0123456789 9\n");
440 term.append("------------\n");
441
442 term.set_text_fg_color(Color::Green);
443 term.plot_char('A', 0, 0);
444 term.plot_char('B', 1, 1);
445 term.plot_char('C', 2, 2);
446 term.plot_char('D', 3, 3);
447 term.plot_char('E', 4, 4);
448 term.plot_char('F', 5, 5);
449 term.set_text_fg_color(Color::XtermWhite);
450
451 assert_eq!(term.cursor_row(), 11);
452 assert_eq!(term.cursor_col(), 0);
453
454 term.set_text_bg_color(Color::DarkBlue);
455 term.plot_char_utf8('b', 8, 1);
456 term.plot_char_utf8('↑', 9, 1);
457 term.plot_char_utf8('c', 8, 2);
458 term.plot_char_utf8('↑', 9, 2);
459 term.plot_char_utf8('d', 8, 3);
460 term.plot_char_utf8('↑', 9, 3);
461 term.plot_char_utf8('e', 8, 4);
462 term.plot_char_utf8('↑', 9, 4);
463 term.plot_char_utf8('f', 8, 5);
464 term.plot_char_utf8('↑', 9, 5);
465 term.plot_char_utf8('g', 8, 6);
466 term.plot_char_utf8('↑', 9, 6);
467 term.set_text_bg_color(Color::TransparentBg);
468
469 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
470 term.append("Done!\n");
471 term.set_text_attrib(Attrib::Normal);
472}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
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?
427fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
428 term.take_focus().unwrap();
429 term.reset_terminal();
430 term.append("0123456789 0\n");
431 term.append("0123456789 1\n");
432 term.append("0123456789 2\n");
433 term.append("0123456789 3\n");
434 term.append("0123456789 4\n");
435 term.append("0123456789 5\n");
436 term.append("0123456789 6\n");
437 term.append("0123456789 7\n");
438 term.append("0123456789 8\n");
439 term.append("0123456789 9\n");
440 term.append("------------\n");
441
442 term.set_text_fg_color(Color::Green);
443 term.plot_char('A', 0, 0);
444 term.plot_char('B', 1, 1);
445 term.plot_char('C', 2, 2);
446 term.plot_char('D', 3, 3);
447 term.plot_char('E', 4, 4);
448 term.plot_char('F', 5, 5);
449 term.set_text_fg_color(Color::XtermWhite);
450
451 assert_eq!(term.cursor_row(), 11);
452 assert_eq!(term.cursor_col(), 0);
453
454 term.set_text_bg_color(Color::DarkBlue);
455 term.plot_char_utf8('b', 8, 1);
456 term.plot_char_utf8('↑', 9, 1);
457 term.plot_char_utf8('c', 8, 2);
458 term.plot_char_utf8('↑', 9, 2);
459 term.plot_char_utf8('d', 8, 3);
460 term.plot_char_utf8('↑', 9, 3);
461 term.plot_char_utf8('e', 8, 4);
462 term.plot_char_utf8('↑', 9, 4);
463 term.plot_char_utf8('f', 8, 5);
464 term.plot_char_utf8('↑', 9, 5);
465 term.plot_char_utf8('g', 8, 6);
466 term.plot_char_utf8('↑', 9, 6);
467 term.set_text_bg_color(Color::TransparentBg);
468
469 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
470 term.append("Done!\n");
471 term.set_text_attrib(Attrib::Normal);
472}
473
474//--------------------------------------------------------------------------------------
475/// More tests that run when the menu bar button Test2 is clicked
476fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
477 term.take_focus().unwrap();
478 term.reset_terminal();
479
480 for i in 0..50 {
481 term.append(&format!("{i}\n"));
482 }
483 assert_eq!(term.history_rows(), 100);
484
485 term.clear_history();
486 assert_eq!(term.history_use(), 0);
487
488 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
489 term.append("\nDone!\n");
490 term.set_text_attrib(Attrib::Normal);
491}
492
493//--------------------------------------------------------------------------------------
494/// Another set of tests that run when Test3 is clicked
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496 term.take_focus().unwrap();
497 term.reset_terminal();
498 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500 assert_eq!(term.history_use(), 0);
501 term.clear();
502 assert_eq!(term.cursor_row(), 0);
503 assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505 term.append("Test\ntext\na\nb\nc\nd");
506 assert_eq!(term.cursor_row(), 5);
507 let hist = term.history_use();
508 term.clear_screen_home(false);
509 assert_eq!(term.cursor_row(), 0);
510 assert_eq!(term.history_use(), hist); // History not changed
511
512 term.append("Test\ntext\na\nb\nc\nd\ne");
513 assert_eq!(term.cursor_row(), 6);
514 term.clear_screen_home(true);
515 assert_eq!(term.cursor_row(), 0);
516
517 term.append("Test\ntext\na\nb\nc\n");
518 assert_eq!(term.cursor_row(), 5);
519 term.clear_to_color(Color::DarkBlue);
520 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521 assert_eq!(term.text_bg_color(), Color::TransparentBg);
522 assert_eq!(term.cursor_row(), 0);
523
524 // Test cursor_home()
525 term.append("Test\n\n\n\n\n\n\n\n\n\n");
526 assert_eq!(term.cursor_row(), 10);
527 term.cursor_home();
528 assert_eq!(term.cursor_row(), 0);
529
530 // Test the widget color
531 assert_eq!(term.color(), Color::Black); // Default
532 term.set_color(Color::DarkGreen);
533 assert_eq!(term.color(), Color::DarkGreen);
534 term.set_color(Color::Black);
535 assert_eq!(term.color(), Color::Black);
536 term.append(
537 "This should be one line of white text on black, embedded into the top of a blue field.\n",
538 );
539
540 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541 term.set_output_translate(OutFlags::OFF);
542 assert_eq!(term.output_translate(), OutFlags::OFF);
543 term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547 term.append("\nDone!\n");
548 term.set_text_attrib(Attrib::Normal);
549}
550
551//--------------------------------------------------------------------------------------
552/// Another set of tests for the ring-buffer access methods
553/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555 let sel_len = term.selection_text_len();
556 let sel = term.selection_text();
557
558 term.take_focus().unwrap();
559 term.reset_terminal();
560 // Test the Utf8Char primitive
561 let uc = Utf8Char::new(b'Q');
562 let uc1 = uc.text_utf8();
563 assert_eq!(&uc1, b"Q");
564 assert_eq!(&uc.attrib(), &Attrib::Normal);
565 assert_eq!(
566 &uc.charflags(),
567 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568 );
569 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570 assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572 let ring_rows = term.ring_rows();
573
574 term.take_focus().unwrap();
575 term.clear_history();
576 assert_eq!(term.history_use(), 0);
577
578 // Subtract row numbers, modulo `rows`
579 fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580 match a - b {
581 n if n < 0 => n + rows,
582 n => n,
583 }
584 }
585 // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587 assert!(term.disp_srow() >= 0);
588 assert!(term.disp_erow() >= 0);
589 assert!(term.hist_srow() >= 0);
590 assert!(term.hist_erow() >= 0);
591 assert!(term.offset() >= 0);
592 assert!(term.disp_srow() <= ring_rows);
593 assert!(term.disp_erow() <= ring_rows);
594 assert!(term.hist_srow() <= ring_rows);
595 assert!(term.hist_erow() <= ring_rows);
596 assert!(term.offset() <= ring_rows);
597
598 assert_eq!(term.ring_srow(), 0);
599 assert_eq!(term.ring_erow(), ring_rows - 1);
600 assert_eq!(
601 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602 term.display_rows()
603 );
604 assert_eq!(
605 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606 term.history_rows()
607 );
608
609 assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610 assert_eq!(term.ring_srow(), 0);
611
612 /// Local function to read back all rows from the display into a long string.
613 /// Does not include scrollback history.
614 /// Trims trailing blanks on each line
615 fn read_disp(term: &Terminal) -> String {
616 let rows = term.display_rows();
617 let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618 for row in 0..rows {
619 let r = term.u8c_disp_row(row).trim();
620 // Iterate through a row, accumulating [u8]
621 for c in r.iter() {
622 // Note: Sometimes utf-8 length is > 1
623 text.extend_from_slice(c.text_utf8());
624 }
625 text.extend_from_slice(b"\n");
626 }
627 // Return the result as a string
628 std::str::from_utf8(&text).unwrap().to_string()
629 }
630
631 term.clear();
632 term.append("Top line ↑ (up-arrow)");
633 term.set_text_attrib(Attrib::Underline);
634 term.append(" ");
635 term.set_text_attrib(Attrib::Normal);
636 term.append(" \n");
637 let mut text_out = read_disp(term);
638 // Trim trailing empty lines
639 text_out = text_out.trim_end_matches(&"\n").to_string();
640 // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642 assert_eq!(text_out, "Top line ↑ (up-arrow) ");
643 let r = term.u8c_disp_row(0);
644 assert_eq!(r.col(0).text_utf8(), b"T");
645 assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646 assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647 let r = term.u8c_disp_row(1);
648 assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649 assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651 // Clear the screen again, then append test text, then read it back and compare
652 let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656 Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659 term.clear_history();
660 term.clear();
661 let bg_save = term.text_bg_color();
662 let fg_save = term.text_fg_color();
663 term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664 term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665 term.append(test_text);
666 term.set_text_bg_color(bg_save);
667 term.set_text_fg_color(fg_save);
668
669 let mut text_out = read_disp(term);
670 // Trim trailing empty lines
671 text_out = text_out.trim_end_matches(&"\n").to_string();
672 assert_eq!(test_text, text_out);
673
674 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676 assert_eq!(term.ring_srow(), 0);
677 assert_eq!(term.ring_erow(), ring_rows - 1);
678 assert_eq!(
679 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680 term.display_rows()
681 );
682 assert_eq!(
683 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684 term.history_rows()
685 );
686
687 term.append(&format!(
688 "\n\nScreen has {} rows of {} columns.\n",
689 term.display_rows(),
690 term.display_columns()
691 ));
692
693 term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
425//--------------------------------------------------------------------------------------
426/// More tests that run when the menu bar Test1 is clicked
427fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
428 term.take_focus().unwrap();
429 term.reset_terminal();
430 term.append("0123456789 0\n");
431 term.append("0123456789 1\n");
432 term.append("0123456789 2\n");
433 term.append("0123456789 3\n");
434 term.append("0123456789 4\n");
435 term.append("0123456789 5\n");
436 term.append("0123456789 6\n");
437 term.append("0123456789 7\n");
438 term.append("0123456789 8\n");
439 term.append("0123456789 9\n");
440 term.append("------------\n");
441
442 term.set_text_fg_color(Color::Green);
443 term.plot_char('A', 0, 0);
444 term.plot_char('B', 1, 1);
445 term.plot_char('C', 2, 2);
446 term.plot_char('D', 3, 3);
447 term.plot_char('E', 4, 4);
448 term.plot_char('F', 5, 5);
449 term.set_text_fg_color(Color::XtermWhite);
450
451 assert_eq!(term.cursor_row(), 11);
452 assert_eq!(term.cursor_col(), 0);
453
454 term.set_text_bg_color(Color::DarkBlue);
455 term.plot_char_utf8('b', 8, 1);
456 term.plot_char_utf8('↑', 9, 1);
457 term.plot_char_utf8('c', 8, 2);
458 term.plot_char_utf8('↑', 9, 2);
459 term.plot_char_utf8('d', 8, 3);
460 term.plot_char_utf8('↑', 9, 3);
461 term.plot_char_utf8('e', 8, 4);
462 term.plot_char_utf8('↑', 9, 4);
463 term.plot_char_utf8('f', 8, 5);
464 term.plot_char_utf8('↑', 9, 5);
465 term.plot_char_utf8('g', 8, 6);
466 term.plot_char_utf8('↑', 9, 6);
467 term.set_text_bg_color(Color::TransparentBg);
468
469 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
470 term.append("Done!\n");
471 term.set_text_attrib(Attrib::Normal);
472}
473
474//--------------------------------------------------------------------------------------
475/// More tests that run when the menu bar button Test2 is clicked
476fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
477 term.take_focus().unwrap();
478 term.reset_terminal();
479
480 for i in 0..50 {
481 term.append(&format!("{i}\n"));
482 }
483 assert_eq!(term.history_rows(), 100);
484
485 term.clear_history();
486 assert_eq!(term.history_use(), 0);
487
488 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
489 term.append("\nDone!\n");
490 term.set_text_attrib(Attrib::Normal);
491}
492
493//--------------------------------------------------------------------------------------
494/// Another set of tests that run when Test3 is clicked
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496 term.take_focus().unwrap();
497 term.reset_terminal();
498 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500 assert_eq!(term.history_use(), 0);
501 term.clear();
502 assert_eq!(term.cursor_row(), 0);
503 assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505 term.append("Test\ntext\na\nb\nc\nd");
506 assert_eq!(term.cursor_row(), 5);
507 let hist = term.history_use();
508 term.clear_screen_home(false);
509 assert_eq!(term.cursor_row(), 0);
510 assert_eq!(term.history_use(), hist); // History not changed
511
512 term.append("Test\ntext\na\nb\nc\nd\ne");
513 assert_eq!(term.cursor_row(), 6);
514 term.clear_screen_home(true);
515 assert_eq!(term.cursor_row(), 0);
516
517 term.append("Test\ntext\na\nb\nc\n");
518 assert_eq!(term.cursor_row(), 5);
519 term.clear_to_color(Color::DarkBlue);
520 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521 assert_eq!(term.text_bg_color(), Color::TransparentBg);
522 assert_eq!(term.cursor_row(), 0);
523
524 // Test cursor_home()
525 term.append("Test\n\n\n\n\n\n\n\n\n\n");
526 assert_eq!(term.cursor_row(), 10);
527 term.cursor_home();
528 assert_eq!(term.cursor_row(), 0);
529
530 // Test the widget color
531 assert_eq!(term.color(), Color::Black); // Default
532 term.set_color(Color::DarkGreen);
533 assert_eq!(term.color(), Color::DarkGreen);
534 term.set_color(Color::Black);
535 assert_eq!(term.color(), Color::Black);
536 term.append(
537 "This should be one line of white text on black, embedded into the top of a blue field.\n",
538 );
539
540 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541 term.set_output_translate(OutFlags::OFF);
542 assert_eq!(term.output_translate(), OutFlags::OFF);
543 term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547 term.append("\nDone!\n");
548 term.set_text_attrib(Attrib::Normal);
549}
550
551//--------------------------------------------------------------------------------------
552/// Another set of tests for the ring-buffer access methods
553/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555 let sel_len = term.selection_text_len();
556 let sel = term.selection_text();
557
558 term.take_focus().unwrap();
559 term.reset_terminal();
560 // Test the Utf8Char primitive
561 let uc = Utf8Char::new(b'Q');
562 let uc1 = uc.text_utf8();
563 assert_eq!(&uc1, b"Q");
564 assert_eq!(&uc.attrib(), &Attrib::Normal);
565 assert_eq!(
566 &uc.charflags(),
567 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568 );
569 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570 assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572 let ring_rows = term.ring_rows();
573
574 term.take_focus().unwrap();
575 term.clear_history();
576 assert_eq!(term.history_use(), 0);
577
578 // Subtract row numbers, modulo `rows`
579 fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580 match a - b {
581 n if n < 0 => n + rows,
582 n => n,
583 }
584 }
585 // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587 assert!(term.disp_srow() >= 0);
588 assert!(term.disp_erow() >= 0);
589 assert!(term.hist_srow() >= 0);
590 assert!(term.hist_erow() >= 0);
591 assert!(term.offset() >= 0);
592 assert!(term.disp_srow() <= ring_rows);
593 assert!(term.disp_erow() <= ring_rows);
594 assert!(term.hist_srow() <= ring_rows);
595 assert!(term.hist_erow() <= ring_rows);
596 assert!(term.offset() <= ring_rows);
597
598 assert_eq!(term.ring_srow(), 0);
599 assert_eq!(term.ring_erow(), ring_rows - 1);
600 assert_eq!(
601 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602 term.display_rows()
603 );
604 assert_eq!(
605 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606 term.history_rows()
607 );
608
609 assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610 assert_eq!(term.ring_srow(), 0);
611
612 /// Local function to read back all rows from the display into a long string.
613 /// Does not include scrollback history.
614 /// Trims trailing blanks on each line
615 fn read_disp(term: &Terminal) -> String {
616 let rows = term.display_rows();
617 let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618 for row in 0..rows {
619 let r = term.u8c_disp_row(row).trim();
620 // Iterate through a row, accumulating [u8]
621 for c in r.iter() {
622 // Note: Sometimes utf-8 length is > 1
623 text.extend_from_slice(c.text_utf8());
624 }
625 text.extend_from_slice(b"\n");
626 }
627 // Return the result as a string
628 std::str::from_utf8(&text).unwrap().to_string()
629 }
630
631 term.clear();
632 term.append("Top line ↑ (up-arrow)");
633 term.set_text_attrib(Attrib::Underline);
634 term.append(" ");
635 term.set_text_attrib(Attrib::Normal);
636 term.append(" \n");
637 let mut text_out = read_disp(term);
638 // Trim trailing empty lines
639 text_out = text_out.trim_end_matches(&"\n").to_string();
640 // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642 assert_eq!(text_out, "Top line ↑ (up-arrow) ");
643 let r = term.u8c_disp_row(0);
644 assert_eq!(r.col(0).text_utf8(), b"T");
645 assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646 assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647 let r = term.u8c_disp_row(1);
648 assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649 assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651 // Clear the screen again, then append test text, then read it back and compare
652 let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656 Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659 term.clear_history();
660 term.clear();
661 let bg_save = term.text_bg_color();
662 let fg_save = term.text_fg_color();
663 term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664 term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665 term.append(test_text);
666 term.set_text_bg_color(bg_save);
667 term.set_text_fg_color(fg_save);
668
669 let mut text_out = read_disp(term);
670 // Trim trailing empty lines
671 text_out = text_out.trim_end_matches(&"\n").to_string();
672 assert_eq!(test_text, text_out);
673
674 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676 assert_eq!(term.ring_srow(), 0);
677 assert_eq!(term.ring_erow(), ring_rows - 1);
678 assert_eq!(
679 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680 term.display_rows()
681 );
682 assert_eq!(
683 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684 term.history_rows()
685 );
686
687 term.append(&format!(
688 "\n\nScreen has {} rows of {} columns.\n",
689 term.display_rows(),
690 term.display_columns()
691 ));
692
693 term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
695
696//--------------------------------------------------------------------------------------
697/// Yet another set of tests for misc cursor functions and other stuff
698/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700 term.take_focus().unwrap();
701
702 // Test the attr_fg_color and attr_bg_color methods.
703 // Put a single character 'A' into the buffer and check it
704 term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705 term.set_text_bg_color(Color::TransparentBg);
706 term.set_text_fg_color(Color::XtermWhite);
707 term.append("A");
708 let r = &term.u8c_disp_row(0);
709 let uc = r.col(0);
710 assert_eq!(uc.text_utf8(), b"A");
711 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712 assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713 assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714 assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715 assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717 // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718 term.clear();
719 term.set_text_fg_color_xterm(XtermColor::White);
720 term.set_text_bg_color_xterm(XtermColor::Black);
721 assert_eq!(term.text_attrib(), Attrib::Normal);
722
723 assert!(term.ansi());
724 term.append("B\x1b[32mC\x1b[1mD\n");
725
726 let r = &term.u8c_disp_row(0);
727 let uc = r.col(0);
728 assert_eq!(uc.text_utf8(), b"B");
729 assert!(uc.is_char(b'B'));
730 assert!(!uc.is_char(b'A'));
731 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735 assert_eq!(
736 &uc.charflags(),
737 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738 );
739
740 let uc = r.col(1);
741 assert_eq!(uc.text_utf8(), b"C");
742 assert!(uc.is_char(b'C'));
743 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747 assert_eq!(
748 &uc.charflags(),
749 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750 );
751
752 let uc = r.col(2);
753 assert_eq!(uc.text_utf8(), b"D");
754 assert!(uc.is_char(b'D'));
755 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757 assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759 assert_eq!(
760 &uc.attr_fgcolor(Some(term)),
761 &Color::from_rgb(0x20, 0xf0, 0x20)
762 );
763 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764 assert_eq!(
765 &uc.charflags(),
766 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767 );
768
769 // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770 term.clear();
771 term.set_text_fg_color_xterm(XtermColor::White);
772 term.set_text_bg_color_xterm(XtermColor::Black);
773 term.set_text_attrib(Attrib::Normal);
774 assert_eq!(term.text_attrib(), Attrib::Normal);
775
776 assert!(term.ansi());
777 term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779 let r = &term.u8c_disp_row(0);
780 let uc = r.col(0);
781 assert_eq!(uc.text_utf8(), b"B");
782 assert!(uc.is_char(b'B'));
783 assert!(!uc.is_char(b'A'));
784 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788 assert_eq!(
789 &uc.charflags(),
790 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791 );
792
793 let uc = r.col(1);
794 assert_eq!(uc.text_utf8(), b"C");
795 assert!(uc.is_char(b'C'));
796 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800 assert_eq!(
801 &uc.charflags(),
802 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803 );
804
805 let uc = r.col(2);
806 assert_eq!(uc.text_utf8(), b"D");
807 assert!(uc.is_char(b'D'));
808 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812 assert_eq!(
813 &uc.charflags(),
814 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815 );
816
817 let uc = r.col(3);
818 assert_eq!(uc.text_utf8(), b"E");
819 assert!(uc.is_char(b'E'));
820 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822 assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823 assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824 assert_eq!(
825 &uc.charflags(),
826 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827 );
828
829 // Test some miscellaneous Utf8 constants
830 assert_eq!(uc.length(), 1);
831 assert_eq!(uc.max_utf8(), 4);
832 assert_eq!(uc.pwidth(), 9.0);
833 assert_eq!(uc.pwidth_int(), 9);
834
835 term.set_text_fg_color_xterm(XtermColor::White);
836 term.set_text_bg_color_xterm(XtermColor::Black);
837 term.clear();
838 term.set_text_attrib(Attrib::Normal);
839
840 // Mouse selection functions
841 term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842 term.clear_mouse_selection();
843 assert_eq!(term.get_selection(), None);
844
845 // Play with cursor position
846 term.append("0123456789\n"); // Set up test pattern
847 term.append("ABCDEFGHIJ\n");
848 term.append("abcdefghij\n");
849
850 term.set_cursor_row(1);
851 assert_eq!(term.cursor_row(), 1);
852 term.set_cursor_col(1);
853 assert_eq!(term.cursor_col(), 1);
854 assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856 term.append("----"); // Overwrites text at cursor and moves cursor forward
857 assert_eq!(term.cursor_row(), 1);
858 assert_eq!(term.cursor_col(), 5);
859 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860 term.set_cursor_col(1);
861 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863 term.cursor_up(1, false);
864 assert_eq!(term.cursor_row(), 0);
865 assert_eq!(term.cursor_col(), 1);
866 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868 // Hit top of screen, so nothing happens
869 term.cursor_up(1, false);
870 assert_eq!(term.cursor_row(), 0);
871 assert_eq!(term.cursor_col(), 1);
872 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874 // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875 term.cursor_up(1, true);
876 assert_eq!(term.cursor_row(), 0);
877 assert_eq!(term.cursor_col(), 1);
878 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880 // Go back down to the overwritten text
881 term.cursor_down(2, false);
882 assert_eq!(term.cursor_row(), 2);
883 assert_eq!(term.cursor_col(), 1);
884 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886 // Go right past the overwritten text
887 term.cursor_right(4, false);
888 assert_eq!(term.cursor_row(), 2);
889 assert_eq!(term.cursor_col(), 5);
890 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892 // Go left to the end of the overwritten text
893 term.cursor_left(1);
894 assert_eq!(term.cursor_row(), 2);
895 assert_eq!(term.cursor_col(), 4);
896 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898 // Scroll back down, removing the blank line at the top.
899 // Cursor stays in place, the text moves under it.
900 term.scroll(1);
901 assert_eq!(term.cursor_row(), 2);
902 assert_eq!(term.cursor_col(), 4);
903 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905 // Clear from here to end-of-line
906 term.clear_eol();
907 assert_eq!(term.cursor_row(), 2);
908 assert_eq!(term.cursor_col(), 4);
909 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911 // Now clear from here to start-of-line. Cursor does not move.
912 term.clear_sol();
913 assert_eq!(term.cursor_row(), 2);
914 assert_eq!(term.cursor_col(), 4);
915 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916 term.cursor_left(1);
917 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918 term.set_cursor_col(0);
919 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921 // Clear some lines
922 term.clear_line(1);
923 assert_eq!(term.cursor_row(), 2);
924 assert_eq!(term.cursor_col(), 0);
925 term.set_cursor_row(1);
926 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927 term.set_cursor_row(3);
928 term.clear_cur_line();
929 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930 assert_eq!(term.cursor_row(), 3);
931 assert_eq!(term.cursor_col(), 0);
932
933 term.append("Two lines above are intentionally left blank.\n");
934 assert_eq!(term.cursor_row(), 4);
935 assert_eq!(term.cursor_col(), 0);
936
937 // Set up the test pattern again, then play with insert/delete
938 term.append("0123456789\n");
939 term.append("ABCDEFGHIJ\n");
940 term.append("abcdefghij\n");
941 assert_eq!(term.cursor_row(), 7);
942
943 term.set_cursor_row(4);
944 term.set_cursor_col(4);
945 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947 term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949 term.cursor_right(5, false);
950 assert_eq!(term.cursor_col(), 9);
951 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953 // Insert two blank rows above cursor. Cursor stays put.
954 term.insert_rows(2);
955 assert_eq!(term.cursor_row(), 4);
956 assert_eq!(term.cursor_col(), 9);
957 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958 term.cursor_down(2, false); // Go down to find our text again
959 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961 // Go back to the beginning of the inserted 'x' characters and delete them.
962 term.cursor_left(5);
963 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964 term.delete_cur_chars(5);
965 assert_eq!(term.cursor_row(), 6);
966 assert_eq!(term.cursor_col(), 4);
967 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969 term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970 term.cursor_down(1, false);
971 term.cursor_left(2);
972 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974 term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975 assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976 term.cursor_up(1, false);
977 term.delete_rows(2); // Delete remains of test pattern
978
979 term.set_text_attrib(Attrib::Bold);
980 term.insert_char_eol('-', 3, 15, 20);
981 term.set_cursor_row(3);
982 term.set_cursor_col(15);
983 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984 assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986 term.set_text_attrib(Attrib::Italic);
987 term.append(" and all lines below");
988 term.set_text_attrib(Attrib::Normal);
989 term.cursor_down(1, false);
990
991 let mut hsb = term.hscrollbar();
992 let mut sb = term.scrollbar();
993 hsb.set_value(100.0);
994 assert_eq!(hsb.value(), 100.0);
995 sb.set_value(50.0);
996 assert_eq!(sb.value(), 50.0);
997 hsb.set_value(0.0);
998 assert_eq!(hsb.value(), 0.0);
999 sb.set_value(0.0);
1000 assert_eq!(sb.value(), 0.0);
1001}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
425//--------------------------------------------------------------------------------------
426/// More tests that run when the menu bar Test1 is clicked
427fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
428 term.take_focus().unwrap();
429 term.reset_terminal();
430 term.append("0123456789 0\n");
431 term.append("0123456789 1\n");
432 term.append("0123456789 2\n");
433 term.append("0123456789 3\n");
434 term.append("0123456789 4\n");
435 term.append("0123456789 5\n");
436 term.append("0123456789 6\n");
437 term.append("0123456789 7\n");
438 term.append("0123456789 8\n");
439 term.append("0123456789 9\n");
440 term.append("------------\n");
441
442 term.set_text_fg_color(Color::Green);
443 term.plot_char('A', 0, 0);
444 term.plot_char('B', 1, 1);
445 term.plot_char('C', 2, 2);
446 term.plot_char('D', 3, 3);
447 term.plot_char('E', 4, 4);
448 term.plot_char('F', 5, 5);
449 term.set_text_fg_color(Color::XtermWhite);
450
451 assert_eq!(term.cursor_row(), 11);
452 assert_eq!(term.cursor_col(), 0);
453
454 term.set_text_bg_color(Color::DarkBlue);
455 term.plot_char_utf8('b', 8, 1);
456 term.plot_char_utf8('↑', 9, 1);
457 term.plot_char_utf8('c', 8, 2);
458 term.plot_char_utf8('↑', 9, 2);
459 term.plot_char_utf8('d', 8, 3);
460 term.plot_char_utf8('↑', 9, 3);
461 term.plot_char_utf8('e', 8, 4);
462 term.plot_char_utf8('↑', 9, 4);
463 term.plot_char_utf8('f', 8, 5);
464 term.plot_char_utf8('↑', 9, 5);
465 term.plot_char_utf8('g', 8, 6);
466 term.plot_char_utf8('↑', 9, 6);
467 term.set_text_bg_color(Color::TransparentBg);
468
469 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
470 term.append("Done!\n");
471 term.set_text_attrib(Attrib::Normal);
472}
473
474//--------------------------------------------------------------------------------------
475/// More tests that run when the menu bar button Test2 is clicked
476fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
477 term.take_focus().unwrap();
478 term.reset_terminal();
479
480 for i in 0..50 {
481 term.append(&format!("{i}\n"));
482 }
483 assert_eq!(term.history_rows(), 100);
484
485 term.clear_history();
486 assert_eq!(term.history_use(), 0);
487
488 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
489 term.append("\nDone!\n");
490 term.set_text_attrib(Attrib::Normal);
491}
492
493//--------------------------------------------------------------------------------------
494/// Another set of tests that run when Test3 is clicked
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496 term.take_focus().unwrap();
497 term.reset_terminal();
498 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500 assert_eq!(term.history_use(), 0);
501 term.clear();
502 assert_eq!(term.cursor_row(), 0);
503 assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505 term.append("Test\ntext\na\nb\nc\nd");
506 assert_eq!(term.cursor_row(), 5);
507 let hist = term.history_use();
508 term.clear_screen_home(false);
509 assert_eq!(term.cursor_row(), 0);
510 assert_eq!(term.history_use(), hist); // History not changed
511
512 term.append("Test\ntext\na\nb\nc\nd\ne");
513 assert_eq!(term.cursor_row(), 6);
514 term.clear_screen_home(true);
515 assert_eq!(term.cursor_row(), 0);
516
517 term.append("Test\ntext\na\nb\nc\n");
518 assert_eq!(term.cursor_row(), 5);
519 term.clear_to_color(Color::DarkBlue);
520 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521 assert_eq!(term.text_bg_color(), Color::TransparentBg);
522 assert_eq!(term.cursor_row(), 0);
523
524 // Test cursor_home()
525 term.append("Test\n\n\n\n\n\n\n\n\n\n");
526 assert_eq!(term.cursor_row(), 10);
527 term.cursor_home();
528 assert_eq!(term.cursor_row(), 0);
529
530 // Test the widget color
531 assert_eq!(term.color(), Color::Black); // Default
532 term.set_color(Color::DarkGreen);
533 assert_eq!(term.color(), Color::DarkGreen);
534 term.set_color(Color::Black);
535 assert_eq!(term.color(), Color::Black);
536 term.append(
537 "This should be one line of white text on black, embedded into the top of a blue field.\n",
538 );
539
540 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541 term.set_output_translate(OutFlags::OFF);
542 assert_eq!(term.output_translate(), OutFlags::OFF);
543 term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547 term.append("\nDone!\n");
548 term.set_text_attrib(Attrib::Normal);
549}
550
551//--------------------------------------------------------------------------------------
552/// Another set of tests for the ring-buffer access methods
553/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555 let sel_len = term.selection_text_len();
556 let sel = term.selection_text();
557
558 term.take_focus().unwrap();
559 term.reset_terminal();
560 // Test the Utf8Char primitive
561 let uc = Utf8Char::new(b'Q');
562 let uc1 = uc.text_utf8();
563 assert_eq!(&uc1, b"Q");
564 assert_eq!(&uc.attrib(), &Attrib::Normal);
565 assert_eq!(
566 &uc.charflags(),
567 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568 );
569 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570 assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572 let ring_rows = term.ring_rows();
573
574 term.take_focus().unwrap();
575 term.clear_history();
576 assert_eq!(term.history_use(), 0);
577
578 // Subtract row numbers, modulo `rows`
579 fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580 match a - b {
581 n if n < 0 => n + rows,
582 n => n,
583 }
584 }
585 // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587 assert!(term.disp_srow() >= 0);
588 assert!(term.disp_erow() >= 0);
589 assert!(term.hist_srow() >= 0);
590 assert!(term.hist_erow() >= 0);
591 assert!(term.offset() >= 0);
592 assert!(term.disp_srow() <= ring_rows);
593 assert!(term.disp_erow() <= ring_rows);
594 assert!(term.hist_srow() <= ring_rows);
595 assert!(term.hist_erow() <= ring_rows);
596 assert!(term.offset() <= ring_rows);
597
598 assert_eq!(term.ring_srow(), 0);
599 assert_eq!(term.ring_erow(), ring_rows - 1);
600 assert_eq!(
601 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602 term.display_rows()
603 );
604 assert_eq!(
605 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606 term.history_rows()
607 );
608
609 assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610 assert_eq!(term.ring_srow(), 0);
611
612 /// Local function to read back all rows from the display into a long string.
613 /// Does not include scrollback history.
614 /// Trims trailing blanks on each line
615 fn read_disp(term: &Terminal) -> String {
616 let rows = term.display_rows();
617 let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618 for row in 0..rows {
619 let r = term.u8c_disp_row(row).trim();
620 // Iterate through a row, accumulating [u8]
621 for c in r.iter() {
622 // Note: Sometimes utf-8 length is > 1
623 text.extend_from_slice(c.text_utf8());
624 }
625 text.extend_from_slice(b"\n");
626 }
627 // Return the result as a string
628 std::str::from_utf8(&text).unwrap().to_string()
629 }
630
631 term.clear();
632 term.append("Top line ↑ (up-arrow)");
633 term.set_text_attrib(Attrib::Underline);
634 term.append(" ");
635 term.set_text_attrib(Attrib::Normal);
636 term.append(" \n");
637 let mut text_out = read_disp(term);
638 // Trim trailing empty lines
639 text_out = text_out.trim_end_matches(&"\n").to_string();
640 // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642 assert_eq!(text_out, "Top line ↑ (up-arrow) ");
643 let r = term.u8c_disp_row(0);
644 assert_eq!(r.col(0).text_utf8(), b"T");
645 assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646 assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647 let r = term.u8c_disp_row(1);
648 assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649 assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651 // Clear the screen again, then append test text, then read it back and compare
652 let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656 Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659 term.clear_history();
660 term.clear();
661 let bg_save = term.text_bg_color();
662 let fg_save = term.text_fg_color();
663 term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664 term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665 term.append(test_text);
666 term.set_text_bg_color(bg_save);
667 term.set_text_fg_color(fg_save);
668
669 let mut text_out = read_disp(term);
670 // Trim trailing empty lines
671 text_out = text_out.trim_end_matches(&"\n").to_string();
672 assert_eq!(test_text, text_out);
673
674 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676 assert_eq!(term.ring_srow(), 0);
677 assert_eq!(term.ring_erow(), ring_rows - 1);
678 assert_eq!(
679 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680 term.display_rows()
681 );
682 assert_eq!(
683 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684 term.history_rows()
685 );
686
687 term.append(&format!(
688 "\n\nScreen has {} rows of {} columns.\n",
689 term.display_rows(),
690 term.display_columns()
691 ));
692
693 term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
695
696//--------------------------------------------------------------------------------------
697/// Yet another set of tests for misc cursor functions and other stuff
698/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700 term.take_focus().unwrap();
701
702 // Test the attr_fg_color and attr_bg_color methods.
703 // Put a single character 'A' into the buffer and check it
704 term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705 term.set_text_bg_color(Color::TransparentBg);
706 term.set_text_fg_color(Color::XtermWhite);
707 term.append("A");
708 let r = &term.u8c_disp_row(0);
709 let uc = r.col(0);
710 assert_eq!(uc.text_utf8(), b"A");
711 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712 assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713 assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714 assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715 assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717 // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718 term.clear();
719 term.set_text_fg_color_xterm(XtermColor::White);
720 term.set_text_bg_color_xterm(XtermColor::Black);
721 assert_eq!(term.text_attrib(), Attrib::Normal);
722
723 assert!(term.ansi());
724 term.append("B\x1b[32mC\x1b[1mD\n");
725
726 let r = &term.u8c_disp_row(0);
727 let uc = r.col(0);
728 assert_eq!(uc.text_utf8(), b"B");
729 assert!(uc.is_char(b'B'));
730 assert!(!uc.is_char(b'A'));
731 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735 assert_eq!(
736 &uc.charflags(),
737 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738 );
739
740 let uc = r.col(1);
741 assert_eq!(uc.text_utf8(), b"C");
742 assert!(uc.is_char(b'C'));
743 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747 assert_eq!(
748 &uc.charflags(),
749 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750 );
751
752 let uc = r.col(2);
753 assert_eq!(uc.text_utf8(), b"D");
754 assert!(uc.is_char(b'D'));
755 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757 assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759 assert_eq!(
760 &uc.attr_fgcolor(Some(term)),
761 &Color::from_rgb(0x20, 0xf0, 0x20)
762 );
763 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764 assert_eq!(
765 &uc.charflags(),
766 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767 );
768
769 // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770 term.clear();
771 term.set_text_fg_color_xterm(XtermColor::White);
772 term.set_text_bg_color_xterm(XtermColor::Black);
773 term.set_text_attrib(Attrib::Normal);
774 assert_eq!(term.text_attrib(), Attrib::Normal);
775
776 assert!(term.ansi());
777 term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779 let r = &term.u8c_disp_row(0);
780 let uc = r.col(0);
781 assert_eq!(uc.text_utf8(), b"B");
782 assert!(uc.is_char(b'B'));
783 assert!(!uc.is_char(b'A'));
784 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788 assert_eq!(
789 &uc.charflags(),
790 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791 );
792
793 let uc = r.col(1);
794 assert_eq!(uc.text_utf8(), b"C");
795 assert!(uc.is_char(b'C'));
796 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800 assert_eq!(
801 &uc.charflags(),
802 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803 );
804
805 let uc = r.col(2);
806 assert_eq!(uc.text_utf8(), b"D");
807 assert!(uc.is_char(b'D'));
808 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812 assert_eq!(
813 &uc.charflags(),
814 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815 );
816
817 let uc = r.col(3);
818 assert_eq!(uc.text_utf8(), b"E");
819 assert!(uc.is_char(b'E'));
820 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822 assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823 assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824 assert_eq!(
825 &uc.charflags(),
826 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827 );
828
829 // Test some miscellaneous Utf8 constants
830 assert_eq!(uc.length(), 1);
831 assert_eq!(uc.max_utf8(), 4);
832 assert_eq!(uc.pwidth(), 9.0);
833 assert_eq!(uc.pwidth_int(), 9);
834
835 term.set_text_fg_color_xterm(XtermColor::White);
836 term.set_text_bg_color_xterm(XtermColor::Black);
837 term.clear();
838 term.set_text_attrib(Attrib::Normal);
839
840 // Mouse selection functions
841 term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842 term.clear_mouse_selection();
843 assert_eq!(term.get_selection(), None);
844
845 // Play with cursor position
846 term.append("0123456789\n"); // Set up test pattern
847 term.append("ABCDEFGHIJ\n");
848 term.append("abcdefghij\n");
849
850 term.set_cursor_row(1);
851 assert_eq!(term.cursor_row(), 1);
852 term.set_cursor_col(1);
853 assert_eq!(term.cursor_col(), 1);
854 assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856 term.append("----"); // Overwrites text at cursor and moves cursor forward
857 assert_eq!(term.cursor_row(), 1);
858 assert_eq!(term.cursor_col(), 5);
859 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860 term.set_cursor_col(1);
861 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863 term.cursor_up(1, false);
864 assert_eq!(term.cursor_row(), 0);
865 assert_eq!(term.cursor_col(), 1);
866 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868 // Hit top of screen, so nothing happens
869 term.cursor_up(1, false);
870 assert_eq!(term.cursor_row(), 0);
871 assert_eq!(term.cursor_col(), 1);
872 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874 // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875 term.cursor_up(1, true);
876 assert_eq!(term.cursor_row(), 0);
877 assert_eq!(term.cursor_col(), 1);
878 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880 // Go back down to the overwritten text
881 term.cursor_down(2, false);
882 assert_eq!(term.cursor_row(), 2);
883 assert_eq!(term.cursor_col(), 1);
884 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886 // Go right past the overwritten text
887 term.cursor_right(4, false);
888 assert_eq!(term.cursor_row(), 2);
889 assert_eq!(term.cursor_col(), 5);
890 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892 // Go left to the end of the overwritten text
893 term.cursor_left(1);
894 assert_eq!(term.cursor_row(), 2);
895 assert_eq!(term.cursor_col(), 4);
896 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898 // Scroll back down, removing the blank line at the top.
899 // Cursor stays in place, the text moves under it.
900 term.scroll(1);
901 assert_eq!(term.cursor_row(), 2);
902 assert_eq!(term.cursor_col(), 4);
903 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905 // Clear from here to end-of-line
906 term.clear_eol();
907 assert_eq!(term.cursor_row(), 2);
908 assert_eq!(term.cursor_col(), 4);
909 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911 // Now clear from here to start-of-line. Cursor does not move.
912 term.clear_sol();
913 assert_eq!(term.cursor_row(), 2);
914 assert_eq!(term.cursor_col(), 4);
915 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916 term.cursor_left(1);
917 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918 term.set_cursor_col(0);
919 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921 // Clear some lines
922 term.clear_line(1);
923 assert_eq!(term.cursor_row(), 2);
924 assert_eq!(term.cursor_col(), 0);
925 term.set_cursor_row(1);
926 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927 term.set_cursor_row(3);
928 term.clear_cur_line();
929 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930 assert_eq!(term.cursor_row(), 3);
931 assert_eq!(term.cursor_col(), 0);
932
933 term.append("Two lines above are intentionally left blank.\n");
934 assert_eq!(term.cursor_row(), 4);
935 assert_eq!(term.cursor_col(), 0);
936
937 // Set up the test pattern again, then play with insert/delete
938 term.append("0123456789\n");
939 term.append("ABCDEFGHIJ\n");
940 term.append("abcdefghij\n");
941 assert_eq!(term.cursor_row(), 7);
942
943 term.set_cursor_row(4);
944 term.set_cursor_col(4);
945 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947 term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949 term.cursor_right(5, false);
950 assert_eq!(term.cursor_col(), 9);
951 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953 // Insert two blank rows above cursor. Cursor stays put.
954 term.insert_rows(2);
955 assert_eq!(term.cursor_row(), 4);
956 assert_eq!(term.cursor_col(), 9);
957 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958 term.cursor_down(2, false); // Go down to find our text again
959 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961 // Go back to the beginning of the inserted 'x' characters and delete them.
962 term.cursor_left(5);
963 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964 term.delete_cur_chars(5);
965 assert_eq!(term.cursor_row(), 6);
966 assert_eq!(term.cursor_col(), 4);
967 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969 term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970 term.cursor_down(1, false);
971 term.cursor_left(2);
972 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974 term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975 assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976 term.cursor_up(1, false);
977 term.delete_rows(2); // Delete remains of test pattern
978
979 term.set_text_attrib(Attrib::Bold);
980 term.insert_char_eol('-', 3, 15, 20);
981 term.set_cursor_row(3);
982 term.set_cursor_col(15);
983 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984 assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986 term.set_text_attrib(Attrib::Italic);
987 term.append(" and all lines below");
988 term.set_text_attrib(Attrib::Normal);
989 term.cursor_down(1, false);
990
991 let mut hsb = term.hscrollbar();
992 let mut sb = term.scrollbar();
993 hsb.set_value(100.0);
994 assert_eq!(hsb.value(), 100.0);
995 sb.set_value(50.0);
996 assert_eq!(sb.value(), 50.0);
997 hsb.set_value(0.0);
998 assert_eq!(hsb.value(), 0.0);
999 sb.set_value(0.0);
1000 assert_eq!(sb.value(), 0.0);
1001}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
425//--------------------------------------------------------------------------------------
426/// More tests that run when the menu bar Test1 is clicked
427fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
428 term.take_focus().unwrap();
429 term.reset_terminal();
430 term.append("0123456789 0\n");
431 term.append("0123456789 1\n");
432 term.append("0123456789 2\n");
433 term.append("0123456789 3\n");
434 term.append("0123456789 4\n");
435 term.append("0123456789 5\n");
436 term.append("0123456789 6\n");
437 term.append("0123456789 7\n");
438 term.append("0123456789 8\n");
439 term.append("0123456789 9\n");
440 term.append("------------\n");
441
442 term.set_text_fg_color(Color::Green);
443 term.plot_char('A', 0, 0);
444 term.plot_char('B', 1, 1);
445 term.plot_char('C', 2, 2);
446 term.plot_char('D', 3, 3);
447 term.plot_char('E', 4, 4);
448 term.plot_char('F', 5, 5);
449 term.set_text_fg_color(Color::XtermWhite);
450
451 assert_eq!(term.cursor_row(), 11);
452 assert_eq!(term.cursor_col(), 0);
453
454 term.set_text_bg_color(Color::DarkBlue);
455 term.plot_char_utf8('b', 8, 1);
456 term.plot_char_utf8('↑', 9, 1);
457 term.plot_char_utf8('c', 8, 2);
458 term.plot_char_utf8('↑', 9, 2);
459 term.plot_char_utf8('d', 8, 3);
460 term.plot_char_utf8('↑', 9, 3);
461 term.plot_char_utf8('e', 8, 4);
462 term.plot_char_utf8('↑', 9, 4);
463 term.plot_char_utf8('f', 8, 5);
464 term.plot_char_utf8('↑', 9, 5);
465 term.plot_char_utf8('g', 8, 6);
466 term.plot_char_utf8('↑', 9, 6);
467 term.set_text_bg_color(Color::TransparentBg);
468
469 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
470 term.append("Done!\n");
471 term.set_text_attrib(Attrib::Normal);
472}
473
474//--------------------------------------------------------------------------------------
475/// More tests that run when the menu bar button Test2 is clicked
476fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
477 term.take_focus().unwrap();
478 term.reset_terminal();
479
480 for i in 0..50 {
481 term.append(&format!("{i}\n"));
482 }
483 assert_eq!(term.history_rows(), 100);
484
485 term.clear_history();
486 assert_eq!(term.history_use(), 0);
487
488 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
489 term.append("\nDone!\n");
490 term.set_text_attrib(Attrib::Normal);
491}
492
493//--------------------------------------------------------------------------------------
494/// Another set of tests that run when Test3 is clicked
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496 term.take_focus().unwrap();
497 term.reset_terminal();
498 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500 assert_eq!(term.history_use(), 0);
501 term.clear();
502 assert_eq!(term.cursor_row(), 0);
503 assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505 term.append("Test\ntext\na\nb\nc\nd");
506 assert_eq!(term.cursor_row(), 5);
507 let hist = term.history_use();
508 term.clear_screen_home(false);
509 assert_eq!(term.cursor_row(), 0);
510 assert_eq!(term.history_use(), hist); // History not changed
511
512 term.append("Test\ntext\na\nb\nc\nd\ne");
513 assert_eq!(term.cursor_row(), 6);
514 term.clear_screen_home(true);
515 assert_eq!(term.cursor_row(), 0);
516
517 term.append("Test\ntext\na\nb\nc\n");
518 assert_eq!(term.cursor_row(), 5);
519 term.clear_to_color(Color::DarkBlue);
520 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521 assert_eq!(term.text_bg_color(), Color::TransparentBg);
522 assert_eq!(term.cursor_row(), 0);
523
524 // Test cursor_home()
525 term.append("Test\n\n\n\n\n\n\n\n\n\n");
526 assert_eq!(term.cursor_row(), 10);
527 term.cursor_home();
528 assert_eq!(term.cursor_row(), 0);
529
530 // Test the widget color
531 assert_eq!(term.color(), Color::Black); // Default
532 term.set_color(Color::DarkGreen);
533 assert_eq!(term.color(), Color::DarkGreen);
534 term.set_color(Color::Black);
535 assert_eq!(term.color(), Color::Black);
536 term.append(
537 "This should be one line of white text on black, embedded into the top of a blue field.\n",
538 );
539
540 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541 term.set_output_translate(OutFlags::OFF);
542 assert_eq!(term.output_translate(), OutFlags::OFF);
543 term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547 term.append("\nDone!\n");
548 term.set_text_attrib(Attrib::Normal);
549}
550
551//--------------------------------------------------------------------------------------
552/// Another set of tests for the ring-buffer access methods
553/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555 let sel_len = term.selection_text_len();
556 let sel = term.selection_text();
557
558 term.take_focus().unwrap();
559 term.reset_terminal();
560 // Test the Utf8Char primitive
561 let uc = Utf8Char::new(b'Q');
562 let uc1 = uc.text_utf8();
563 assert_eq!(&uc1, b"Q");
564 assert_eq!(&uc.attrib(), &Attrib::Normal);
565 assert_eq!(
566 &uc.charflags(),
567 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568 );
569 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570 assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572 let ring_rows = term.ring_rows();
573
574 term.take_focus().unwrap();
575 term.clear_history();
576 assert_eq!(term.history_use(), 0);
577
578 // Subtract row numbers, modulo `rows`
579 fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580 match a - b {
581 n if n < 0 => n + rows,
582 n => n,
583 }
584 }
585 // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587 assert!(term.disp_srow() >= 0);
588 assert!(term.disp_erow() >= 0);
589 assert!(term.hist_srow() >= 0);
590 assert!(term.hist_erow() >= 0);
591 assert!(term.offset() >= 0);
592 assert!(term.disp_srow() <= ring_rows);
593 assert!(term.disp_erow() <= ring_rows);
594 assert!(term.hist_srow() <= ring_rows);
595 assert!(term.hist_erow() <= ring_rows);
596 assert!(term.offset() <= ring_rows);
597
598 assert_eq!(term.ring_srow(), 0);
599 assert_eq!(term.ring_erow(), ring_rows - 1);
600 assert_eq!(
601 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602 term.display_rows()
603 );
604 assert_eq!(
605 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606 term.history_rows()
607 );
608
609 assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610 assert_eq!(term.ring_srow(), 0);
611
612 /// Local function to read back all rows from the display into a long string.
613 /// Does not include scrollback history.
614 /// Trims trailing blanks on each line
615 fn read_disp(term: &Terminal) -> String {
616 let rows = term.display_rows();
617 let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618 for row in 0..rows {
619 let r = term.u8c_disp_row(row).trim();
620 // Iterate through a row, accumulating [u8]
621 for c in r.iter() {
622 // Note: Sometimes utf-8 length is > 1
623 text.extend_from_slice(c.text_utf8());
624 }
625 text.extend_from_slice(b"\n");
626 }
627 // Return the result as a string
628 std::str::from_utf8(&text).unwrap().to_string()
629 }
630
631 term.clear();
632 term.append("Top line ↑ (up-arrow)");
633 term.set_text_attrib(Attrib::Underline);
634 term.append(" ");
635 term.set_text_attrib(Attrib::Normal);
636 term.append(" \n");
637 let mut text_out = read_disp(term);
638 // Trim trailing empty lines
639 text_out = text_out.trim_end_matches(&"\n").to_string();
640 // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642 assert_eq!(text_out, "Top line ↑ (up-arrow) ");
643 let r = term.u8c_disp_row(0);
644 assert_eq!(r.col(0).text_utf8(), b"T");
645 assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646 assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647 let r = term.u8c_disp_row(1);
648 assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649 assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651 // Clear the screen again, then append test text, then read it back and compare
652 let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656 Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659 term.clear_history();
660 term.clear();
661 let bg_save = term.text_bg_color();
662 let fg_save = term.text_fg_color();
663 term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664 term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665 term.append(test_text);
666 term.set_text_bg_color(bg_save);
667 term.set_text_fg_color(fg_save);
668
669 let mut text_out = read_disp(term);
670 // Trim trailing empty lines
671 text_out = text_out.trim_end_matches(&"\n").to_string();
672 assert_eq!(test_text, text_out);
673
674 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676 assert_eq!(term.ring_srow(), 0);
677 assert_eq!(term.ring_erow(), ring_rows - 1);
678 assert_eq!(
679 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680 term.display_rows()
681 );
682 assert_eq!(
683 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684 term.history_rows()
685 );
686
687 term.append(&format!(
688 "\n\nScreen has {} rows of {} columns.\n",
689 term.display_rows(),
690 term.display_columns()
691 ));
692
693 term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
695
696//--------------------------------------------------------------------------------------
697/// Yet another set of tests for misc cursor functions and other stuff
698/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700 term.take_focus().unwrap();
701
702 // Test the attr_fg_color and attr_bg_color methods.
703 // Put a single character 'A' into the buffer and check it
704 term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705 term.set_text_bg_color(Color::TransparentBg);
706 term.set_text_fg_color(Color::XtermWhite);
707 term.append("A");
708 let r = &term.u8c_disp_row(0);
709 let uc = r.col(0);
710 assert_eq!(uc.text_utf8(), b"A");
711 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712 assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713 assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714 assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715 assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717 // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718 term.clear();
719 term.set_text_fg_color_xterm(XtermColor::White);
720 term.set_text_bg_color_xterm(XtermColor::Black);
721 assert_eq!(term.text_attrib(), Attrib::Normal);
722
723 assert!(term.ansi());
724 term.append("B\x1b[32mC\x1b[1mD\n");
725
726 let r = &term.u8c_disp_row(0);
727 let uc = r.col(0);
728 assert_eq!(uc.text_utf8(), b"B");
729 assert!(uc.is_char(b'B'));
730 assert!(!uc.is_char(b'A'));
731 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735 assert_eq!(
736 &uc.charflags(),
737 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738 );
739
740 let uc = r.col(1);
741 assert_eq!(uc.text_utf8(), b"C");
742 assert!(uc.is_char(b'C'));
743 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747 assert_eq!(
748 &uc.charflags(),
749 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750 );
751
752 let uc = r.col(2);
753 assert_eq!(uc.text_utf8(), b"D");
754 assert!(uc.is_char(b'D'));
755 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757 assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759 assert_eq!(
760 &uc.attr_fgcolor(Some(term)),
761 &Color::from_rgb(0x20, 0xf0, 0x20)
762 );
763 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764 assert_eq!(
765 &uc.charflags(),
766 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767 );
768
769 // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770 term.clear();
771 term.set_text_fg_color_xterm(XtermColor::White);
772 term.set_text_bg_color_xterm(XtermColor::Black);
773 term.set_text_attrib(Attrib::Normal);
774 assert_eq!(term.text_attrib(), Attrib::Normal);
775
776 assert!(term.ansi());
777 term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779 let r = &term.u8c_disp_row(0);
780 let uc = r.col(0);
781 assert_eq!(uc.text_utf8(), b"B");
782 assert!(uc.is_char(b'B'));
783 assert!(!uc.is_char(b'A'));
784 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788 assert_eq!(
789 &uc.charflags(),
790 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791 );
792
793 let uc = r.col(1);
794 assert_eq!(uc.text_utf8(), b"C");
795 assert!(uc.is_char(b'C'));
796 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800 assert_eq!(
801 &uc.charflags(),
802 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803 );
804
805 let uc = r.col(2);
806 assert_eq!(uc.text_utf8(), b"D");
807 assert!(uc.is_char(b'D'));
808 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812 assert_eq!(
813 &uc.charflags(),
814 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815 );
816
817 let uc = r.col(3);
818 assert_eq!(uc.text_utf8(), b"E");
819 assert!(uc.is_char(b'E'));
820 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822 assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823 assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824 assert_eq!(
825 &uc.charflags(),
826 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827 );
828
829 // Test some miscellaneous Utf8 constants
830 assert_eq!(uc.length(), 1);
831 assert_eq!(uc.max_utf8(), 4);
832 assert_eq!(uc.pwidth(), 9.0);
833 assert_eq!(uc.pwidth_int(), 9);
834
835 term.set_text_fg_color_xterm(XtermColor::White);
836 term.set_text_bg_color_xterm(XtermColor::Black);
837 term.clear();
838 term.set_text_attrib(Attrib::Normal);
839
840 // Mouse selection functions
841 term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842 term.clear_mouse_selection();
843 assert_eq!(term.get_selection(), None);
844
845 // Play with cursor position
846 term.append("0123456789\n"); // Set up test pattern
847 term.append("ABCDEFGHIJ\n");
848 term.append("abcdefghij\n");
849
850 term.set_cursor_row(1);
851 assert_eq!(term.cursor_row(), 1);
852 term.set_cursor_col(1);
853 assert_eq!(term.cursor_col(), 1);
854 assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856 term.append("----"); // Overwrites text at cursor and moves cursor forward
857 assert_eq!(term.cursor_row(), 1);
858 assert_eq!(term.cursor_col(), 5);
859 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860 term.set_cursor_col(1);
861 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863 term.cursor_up(1, false);
864 assert_eq!(term.cursor_row(), 0);
865 assert_eq!(term.cursor_col(), 1);
866 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868 // Hit top of screen, so nothing happens
869 term.cursor_up(1, false);
870 assert_eq!(term.cursor_row(), 0);
871 assert_eq!(term.cursor_col(), 1);
872 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874 // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875 term.cursor_up(1, true);
876 assert_eq!(term.cursor_row(), 0);
877 assert_eq!(term.cursor_col(), 1);
878 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880 // Go back down to the overwritten text
881 term.cursor_down(2, false);
882 assert_eq!(term.cursor_row(), 2);
883 assert_eq!(term.cursor_col(), 1);
884 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886 // Go right past the overwritten text
887 term.cursor_right(4, false);
888 assert_eq!(term.cursor_row(), 2);
889 assert_eq!(term.cursor_col(), 5);
890 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892 // Go left to the end of the overwritten text
893 term.cursor_left(1);
894 assert_eq!(term.cursor_row(), 2);
895 assert_eq!(term.cursor_col(), 4);
896 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898 // Scroll back down, removing the blank line at the top.
899 // Cursor stays in place, the text moves under it.
900 term.scroll(1);
901 assert_eq!(term.cursor_row(), 2);
902 assert_eq!(term.cursor_col(), 4);
903 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905 // Clear from here to end-of-line
906 term.clear_eol();
907 assert_eq!(term.cursor_row(), 2);
908 assert_eq!(term.cursor_col(), 4);
909 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911 // Now clear from here to start-of-line. Cursor does not move.
912 term.clear_sol();
913 assert_eq!(term.cursor_row(), 2);
914 assert_eq!(term.cursor_col(), 4);
915 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916 term.cursor_left(1);
917 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918 term.set_cursor_col(0);
919 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921 // Clear some lines
922 term.clear_line(1);
923 assert_eq!(term.cursor_row(), 2);
924 assert_eq!(term.cursor_col(), 0);
925 term.set_cursor_row(1);
926 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927 term.set_cursor_row(3);
928 term.clear_cur_line();
929 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930 assert_eq!(term.cursor_row(), 3);
931 assert_eq!(term.cursor_col(), 0);
932
933 term.append("Two lines above are intentionally left blank.\n");
934 assert_eq!(term.cursor_row(), 4);
935 assert_eq!(term.cursor_col(), 0);
936
937 // Set up the test pattern again, then play with insert/delete
938 term.append("0123456789\n");
939 term.append("ABCDEFGHIJ\n");
940 term.append("abcdefghij\n");
941 assert_eq!(term.cursor_row(), 7);
942
943 term.set_cursor_row(4);
944 term.set_cursor_col(4);
945 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947 term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949 term.cursor_right(5, false);
950 assert_eq!(term.cursor_col(), 9);
951 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953 // Insert two blank rows above cursor. Cursor stays put.
954 term.insert_rows(2);
955 assert_eq!(term.cursor_row(), 4);
956 assert_eq!(term.cursor_col(), 9);
957 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958 term.cursor_down(2, false); // Go down to find our text again
959 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961 // Go back to the beginning of the inserted 'x' characters and delete them.
962 term.cursor_left(5);
963 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964 term.delete_cur_chars(5);
965 assert_eq!(term.cursor_row(), 6);
966 assert_eq!(term.cursor_col(), 4);
967 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969 term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970 term.cursor_down(1, false);
971 term.cursor_left(2);
972 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974 term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975 assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976 term.cursor_up(1, false);
977 term.delete_rows(2); // Delete remains of test pattern
978
979 term.set_text_attrib(Attrib::Bold);
980 term.insert_char_eol('-', 3, 15, 20);
981 term.set_cursor_row(3);
982 term.set_cursor_col(15);
983 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984 assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986 term.set_text_attrib(Attrib::Italic);
987 term.append(" and all lines below");
988 term.set_text_attrib(Attrib::Normal);
989 term.cursor_down(1, false);
990
991 let mut hsb = term.hscrollbar();
992 let mut sb = term.scrollbar();
993 hsb.set_value(100.0);
994 assert_eq!(hsb.value(), 100.0);
995 sb.set_value(50.0);
996 assert_eq!(sb.value(), 50.0);
997 hsb.set_value(0.0);
998 assert_eq!(hsb.value(), 0.0);
999 sb.set_value(0.0);
1000 assert_eq!(sb.value(), 0.0);
1001}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
425//--------------------------------------------------------------------------------------
426/// More tests that run when the menu bar Test1 is clicked
427fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
428 term.take_focus().unwrap();
429 term.reset_terminal();
430 term.append("0123456789 0\n");
431 term.append("0123456789 1\n");
432 term.append("0123456789 2\n");
433 term.append("0123456789 3\n");
434 term.append("0123456789 4\n");
435 term.append("0123456789 5\n");
436 term.append("0123456789 6\n");
437 term.append("0123456789 7\n");
438 term.append("0123456789 8\n");
439 term.append("0123456789 9\n");
440 term.append("------------\n");
441
442 term.set_text_fg_color(Color::Green);
443 term.plot_char('A', 0, 0);
444 term.plot_char('B', 1, 1);
445 term.plot_char('C', 2, 2);
446 term.plot_char('D', 3, 3);
447 term.plot_char('E', 4, 4);
448 term.plot_char('F', 5, 5);
449 term.set_text_fg_color(Color::XtermWhite);
450
451 assert_eq!(term.cursor_row(), 11);
452 assert_eq!(term.cursor_col(), 0);
453
454 term.set_text_bg_color(Color::DarkBlue);
455 term.plot_char_utf8('b', 8, 1);
456 term.plot_char_utf8('↑', 9, 1);
457 term.plot_char_utf8('c', 8, 2);
458 term.plot_char_utf8('↑', 9, 2);
459 term.plot_char_utf8('d', 8, 3);
460 term.plot_char_utf8('↑', 9, 3);
461 term.plot_char_utf8('e', 8, 4);
462 term.plot_char_utf8('↑', 9, 4);
463 term.plot_char_utf8('f', 8, 5);
464 term.plot_char_utf8('↑', 9, 5);
465 term.plot_char_utf8('g', 8, 6);
466 term.plot_char_utf8('↑', 9, 6);
467 term.set_text_bg_color(Color::TransparentBg);
468
469 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
470 term.append("Done!\n");
471 term.set_text_attrib(Attrib::Normal);
472}
473
474//--------------------------------------------------------------------------------------
475/// More tests that run when the menu bar button Test2 is clicked
476fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
477 term.take_focus().unwrap();
478 term.reset_terminal();
479
480 for i in 0..50 {
481 term.append(&format!("{i}\n"));
482 }
483 assert_eq!(term.history_rows(), 100);
484
485 term.clear_history();
486 assert_eq!(term.history_use(), 0);
487
488 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
489 term.append("\nDone!\n");
490 term.set_text_attrib(Attrib::Normal);
491}
492
493//--------------------------------------------------------------------------------------
494/// Another set of tests that run when Test3 is clicked
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496 term.take_focus().unwrap();
497 term.reset_terminal();
498 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500 assert_eq!(term.history_use(), 0);
501 term.clear();
502 assert_eq!(term.cursor_row(), 0);
503 assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505 term.append("Test\ntext\na\nb\nc\nd");
506 assert_eq!(term.cursor_row(), 5);
507 let hist = term.history_use();
508 term.clear_screen_home(false);
509 assert_eq!(term.cursor_row(), 0);
510 assert_eq!(term.history_use(), hist); // History not changed
511
512 term.append("Test\ntext\na\nb\nc\nd\ne");
513 assert_eq!(term.cursor_row(), 6);
514 term.clear_screen_home(true);
515 assert_eq!(term.cursor_row(), 0);
516
517 term.append("Test\ntext\na\nb\nc\n");
518 assert_eq!(term.cursor_row(), 5);
519 term.clear_to_color(Color::DarkBlue);
520 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521 assert_eq!(term.text_bg_color(), Color::TransparentBg);
522 assert_eq!(term.cursor_row(), 0);
523
524 // Test cursor_home()
525 term.append("Test\n\n\n\n\n\n\n\n\n\n");
526 assert_eq!(term.cursor_row(), 10);
527 term.cursor_home();
528 assert_eq!(term.cursor_row(), 0);
529
530 // Test the widget color
531 assert_eq!(term.color(), Color::Black); // Default
532 term.set_color(Color::DarkGreen);
533 assert_eq!(term.color(), Color::DarkGreen);
534 term.set_color(Color::Black);
535 assert_eq!(term.color(), Color::Black);
536 term.append(
537 "This should be one line of white text on black, embedded into the top of a blue field.\n",
538 );
539
540 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541 term.set_output_translate(OutFlags::OFF);
542 assert_eq!(term.output_translate(), OutFlags::OFF);
543 term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547 term.append("\nDone!\n");
548 term.set_text_attrib(Attrib::Normal);
549}
550
551//--------------------------------------------------------------------------------------
552/// Another set of tests for the ring-buffer access methods
553/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555 let sel_len = term.selection_text_len();
556 let sel = term.selection_text();
557
558 term.take_focus().unwrap();
559 term.reset_terminal();
560 // Test the Utf8Char primitive
561 let uc = Utf8Char::new(b'Q');
562 let uc1 = uc.text_utf8();
563 assert_eq!(&uc1, b"Q");
564 assert_eq!(&uc.attrib(), &Attrib::Normal);
565 assert_eq!(
566 &uc.charflags(),
567 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568 );
569 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570 assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572 let ring_rows = term.ring_rows();
573
574 term.take_focus().unwrap();
575 term.clear_history();
576 assert_eq!(term.history_use(), 0);
577
578 // Subtract row numbers, modulo `rows`
579 fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580 match a - b {
581 n if n < 0 => n + rows,
582 n => n,
583 }
584 }
585 // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587 assert!(term.disp_srow() >= 0);
588 assert!(term.disp_erow() >= 0);
589 assert!(term.hist_srow() >= 0);
590 assert!(term.hist_erow() >= 0);
591 assert!(term.offset() >= 0);
592 assert!(term.disp_srow() <= ring_rows);
593 assert!(term.disp_erow() <= ring_rows);
594 assert!(term.hist_srow() <= ring_rows);
595 assert!(term.hist_erow() <= ring_rows);
596 assert!(term.offset() <= ring_rows);
597
598 assert_eq!(term.ring_srow(), 0);
599 assert_eq!(term.ring_erow(), ring_rows - 1);
600 assert_eq!(
601 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602 term.display_rows()
603 );
604 assert_eq!(
605 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606 term.history_rows()
607 );
608
609 assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610 assert_eq!(term.ring_srow(), 0);
611
612 /// Local function to read back all rows from the display into a long string.
613 /// Does not include scrollback history.
614 /// Trims trailing blanks on each line
615 fn read_disp(term: &Terminal) -> String {
616 let rows = term.display_rows();
617 let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618 for row in 0..rows {
619 let r = term.u8c_disp_row(row).trim();
620 // Iterate through a row, accumulating [u8]
621 for c in r.iter() {
622 // Note: Sometimes utf-8 length is > 1
623 text.extend_from_slice(c.text_utf8());
624 }
625 text.extend_from_slice(b"\n");
626 }
627 // Return the result as a string
628 std::str::from_utf8(&text).unwrap().to_string()
629 }
630
631 term.clear();
632 term.append("Top line ↑ (up-arrow)");
633 term.set_text_attrib(Attrib::Underline);
634 term.append(" ");
635 term.set_text_attrib(Attrib::Normal);
636 term.append(" \n");
637 let mut text_out = read_disp(term);
638 // Trim trailing empty lines
639 text_out = text_out.trim_end_matches(&"\n").to_string();
640 // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642 assert_eq!(text_out, "Top line ↑ (up-arrow) ");
643 let r = term.u8c_disp_row(0);
644 assert_eq!(r.col(0).text_utf8(), b"T");
645 assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646 assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647 let r = term.u8c_disp_row(1);
648 assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649 assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651 // Clear the screen again, then append test text, then read it back and compare
652 let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656 Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659 term.clear_history();
660 term.clear();
661 let bg_save = term.text_bg_color();
662 let fg_save = term.text_fg_color();
663 term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664 term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665 term.append(test_text);
666 term.set_text_bg_color(bg_save);
667 term.set_text_fg_color(fg_save);
668
669 let mut text_out = read_disp(term);
670 // Trim trailing empty lines
671 text_out = text_out.trim_end_matches(&"\n").to_string();
672 assert_eq!(test_text, text_out);
673
674 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676 assert_eq!(term.ring_srow(), 0);
677 assert_eq!(term.ring_erow(), ring_rows - 1);
678 assert_eq!(
679 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680 term.display_rows()
681 );
682 assert_eq!(
683 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684 term.history_rows()
685 );
686
687 term.append(&format!(
688 "\n\nScreen has {} rows of {} columns.\n",
689 term.display_rows(),
690 term.display_columns()
691 ));
692
693 term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
695
696//--------------------------------------------------------------------------------------
697/// Yet another set of tests for misc cursor functions and other stuff
698/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700 term.take_focus().unwrap();
701
702 // Test the attr_fg_color and attr_bg_color methods.
703 // Put a single character 'A' into the buffer and check it
704 term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705 term.set_text_bg_color(Color::TransparentBg);
706 term.set_text_fg_color(Color::XtermWhite);
707 term.append("A");
708 let r = &term.u8c_disp_row(0);
709 let uc = r.col(0);
710 assert_eq!(uc.text_utf8(), b"A");
711 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712 assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713 assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714 assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715 assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717 // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718 term.clear();
719 term.set_text_fg_color_xterm(XtermColor::White);
720 term.set_text_bg_color_xterm(XtermColor::Black);
721 assert_eq!(term.text_attrib(), Attrib::Normal);
722
723 assert!(term.ansi());
724 term.append("B\x1b[32mC\x1b[1mD\n");
725
726 let r = &term.u8c_disp_row(0);
727 let uc = r.col(0);
728 assert_eq!(uc.text_utf8(), b"B");
729 assert!(uc.is_char(b'B'));
730 assert!(!uc.is_char(b'A'));
731 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735 assert_eq!(
736 &uc.charflags(),
737 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738 );
739
740 let uc = r.col(1);
741 assert_eq!(uc.text_utf8(), b"C");
742 assert!(uc.is_char(b'C'));
743 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747 assert_eq!(
748 &uc.charflags(),
749 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750 );
751
752 let uc = r.col(2);
753 assert_eq!(uc.text_utf8(), b"D");
754 assert!(uc.is_char(b'D'));
755 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757 assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759 assert_eq!(
760 &uc.attr_fgcolor(Some(term)),
761 &Color::from_rgb(0x20, 0xf0, 0x20)
762 );
763 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764 assert_eq!(
765 &uc.charflags(),
766 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767 );
768
769 // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770 term.clear();
771 term.set_text_fg_color_xterm(XtermColor::White);
772 term.set_text_bg_color_xterm(XtermColor::Black);
773 term.set_text_attrib(Attrib::Normal);
774 assert_eq!(term.text_attrib(), Attrib::Normal);
775
776 assert!(term.ansi());
777 term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779 let r = &term.u8c_disp_row(0);
780 let uc = r.col(0);
781 assert_eq!(uc.text_utf8(), b"B");
782 assert!(uc.is_char(b'B'));
783 assert!(!uc.is_char(b'A'));
784 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788 assert_eq!(
789 &uc.charflags(),
790 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791 );
792
793 let uc = r.col(1);
794 assert_eq!(uc.text_utf8(), b"C");
795 assert!(uc.is_char(b'C'));
796 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800 assert_eq!(
801 &uc.charflags(),
802 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803 );
804
805 let uc = r.col(2);
806 assert_eq!(uc.text_utf8(), b"D");
807 assert!(uc.is_char(b'D'));
808 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812 assert_eq!(
813 &uc.charflags(),
814 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815 );
816
817 let uc = r.col(3);
818 assert_eq!(uc.text_utf8(), b"E");
819 assert!(uc.is_char(b'E'));
820 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822 assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823 assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824 assert_eq!(
825 &uc.charflags(),
826 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827 );
828
829 // Test some miscellaneous Utf8 constants
830 assert_eq!(uc.length(), 1);
831 assert_eq!(uc.max_utf8(), 4);
832 assert_eq!(uc.pwidth(), 9.0);
833 assert_eq!(uc.pwidth_int(), 9);
834
835 term.set_text_fg_color_xterm(XtermColor::White);
836 term.set_text_bg_color_xterm(XtermColor::Black);
837 term.clear();
838 term.set_text_attrib(Attrib::Normal);
839
840 // Mouse selection functions
841 term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842 term.clear_mouse_selection();
843 assert_eq!(term.get_selection(), None);
844
845 // Play with cursor position
846 term.append("0123456789\n"); // Set up test pattern
847 term.append("ABCDEFGHIJ\n");
848 term.append("abcdefghij\n");
849
850 term.set_cursor_row(1);
851 assert_eq!(term.cursor_row(), 1);
852 term.set_cursor_col(1);
853 assert_eq!(term.cursor_col(), 1);
854 assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856 term.append("----"); // Overwrites text at cursor and moves cursor forward
857 assert_eq!(term.cursor_row(), 1);
858 assert_eq!(term.cursor_col(), 5);
859 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860 term.set_cursor_col(1);
861 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863 term.cursor_up(1, false);
864 assert_eq!(term.cursor_row(), 0);
865 assert_eq!(term.cursor_col(), 1);
866 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868 // Hit top of screen, so nothing happens
869 term.cursor_up(1, false);
870 assert_eq!(term.cursor_row(), 0);
871 assert_eq!(term.cursor_col(), 1);
872 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874 // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875 term.cursor_up(1, true);
876 assert_eq!(term.cursor_row(), 0);
877 assert_eq!(term.cursor_col(), 1);
878 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880 // Go back down to the overwritten text
881 term.cursor_down(2, false);
882 assert_eq!(term.cursor_row(), 2);
883 assert_eq!(term.cursor_col(), 1);
884 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886 // Go right past the overwritten text
887 term.cursor_right(4, false);
888 assert_eq!(term.cursor_row(), 2);
889 assert_eq!(term.cursor_col(), 5);
890 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892 // Go left to the end of the overwritten text
893 term.cursor_left(1);
894 assert_eq!(term.cursor_row(), 2);
895 assert_eq!(term.cursor_col(), 4);
896 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898 // Scroll back down, removing the blank line at the top.
899 // Cursor stays in place, the text moves under it.
900 term.scroll(1);
901 assert_eq!(term.cursor_row(), 2);
902 assert_eq!(term.cursor_col(), 4);
903 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905 // Clear from here to end-of-line
906 term.clear_eol();
907 assert_eq!(term.cursor_row(), 2);
908 assert_eq!(term.cursor_col(), 4);
909 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911 // Now clear from here to start-of-line. Cursor does not move.
912 term.clear_sol();
913 assert_eq!(term.cursor_row(), 2);
914 assert_eq!(term.cursor_col(), 4);
915 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916 term.cursor_left(1);
917 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918 term.set_cursor_col(0);
919 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921 // Clear some lines
922 term.clear_line(1);
923 assert_eq!(term.cursor_row(), 2);
924 assert_eq!(term.cursor_col(), 0);
925 term.set_cursor_row(1);
926 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927 term.set_cursor_row(3);
928 term.clear_cur_line();
929 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930 assert_eq!(term.cursor_row(), 3);
931 assert_eq!(term.cursor_col(), 0);
932
933 term.append("Two lines above are intentionally left blank.\n");
934 assert_eq!(term.cursor_row(), 4);
935 assert_eq!(term.cursor_col(), 0);
936
937 // Set up the test pattern again, then play with insert/delete
938 term.append("0123456789\n");
939 term.append("ABCDEFGHIJ\n");
940 term.append("abcdefghij\n");
941 assert_eq!(term.cursor_row(), 7);
942
943 term.set_cursor_row(4);
944 term.set_cursor_col(4);
945 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947 term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949 term.cursor_right(5, false);
950 assert_eq!(term.cursor_col(), 9);
951 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953 // Insert two blank rows above cursor. Cursor stays put.
954 term.insert_rows(2);
955 assert_eq!(term.cursor_row(), 4);
956 assert_eq!(term.cursor_col(), 9);
957 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958 term.cursor_down(2, false); // Go down to find our text again
959 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961 // Go back to the beginning of the inserted 'x' characters and delete them.
962 term.cursor_left(5);
963 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964 term.delete_cur_chars(5);
965 assert_eq!(term.cursor_row(), 6);
966 assert_eq!(term.cursor_col(), 4);
967 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969 term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970 term.cursor_down(1, false);
971 term.cursor_left(2);
972 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974 term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975 assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976 term.cursor_up(1, false);
977 term.delete_rows(2); // Delete remains of test pattern
978
979 term.set_text_attrib(Attrib::Bold);
980 term.insert_char_eol('-', 3, 15, 20);
981 term.set_cursor_row(3);
982 term.set_cursor_col(15);
983 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984 assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986 term.set_text_attrib(Attrib::Italic);
987 term.append(" and all lines below");
988 term.set_text_attrib(Attrib::Normal);
989 term.cursor_down(1, false);
990
991 let mut hsb = term.hscrollbar();
992 let mut sb = term.scrollbar();
993 hsb.set_value(100.0);
994 assert_eq!(hsb.value(), 100.0);
995 sb.set_value(50.0);
996 assert_eq!(sb.value(), 50.0);
997 hsb.set_value(0.0);
998 assert_eq!(hsb.value(), 0.0);
999 sb.set_value(0.0);
1000 assert_eq!(sb.value(), 0.0);
1001}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
425//--------------------------------------------------------------------------------------
426/// More tests that run when the menu bar Test1 is clicked
427fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
428 term.take_focus().unwrap();
429 term.reset_terminal();
430 term.append("0123456789 0\n");
431 term.append("0123456789 1\n");
432 term.append("0123456789 2\n");
433 term.append("0123456789 3\n");
434 term.append("0123456789 4\n");
435 term.append("0123456789 5\n");
436 term.append("0123456789 6\n");
437 term.append("0123456789 7\n");
438 term.append("0123456789 8\n");
439 term.append("0123456789 9\n");
440 term.append("------------\n");
441
442 term.set_text_fg_color(Color::Green);
443 term.plot_char('A', 0, 0);
444 term.plot_char('B', 1, 1);
445 term.plot_char('C', 2, 2);
446 term.plot_char('D', 3, 3);
447 term.plot_char('E', 4, 4);
448 term.plot_char('F', 5, 5);
449 term.set_text_fg_color(Color::XtermWhite);
450
451 assert_eq!(term.cursor_row(), 11);
452 assert_eq!(term.cursor_col(), 0);
453
454 term.set_text_bg_color(Color::DarkBlue);
455 term.plot_char_utf8('b', 8, 1);
456 term.plot_char_utf8('↑', 9, 1);
457 term.plot_char_utf8('c', 8, 2);
458 term.plot_char_utf8('↑', 9, 2);
459 term.plot_char_utf8('d', 8, 3);
460 term.plot_char_utf8('↑', 9, 3);
461 term.plot_char_utf8('e', 8, 4);
462 term.plot_char_utf8('↑', 9, 4);
463 term.plot_char_utf8('f', 8, 5);
464 term.plot_char_utf8('↑', 9, 5);
465 term.plot_char_utf8('g', 8, 6);
466 term.plot_char_utf8('↑', 9, 6);
467 term.set_text_bg_color(Color::TransparentBg);
468
469 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
470 term.append("Done!\n");
471 term.set_text_attrib(Attrib::Normal);
472}
473
474//--------------------------------------------------------------------------------------
475/// More tests that run when the menu bar button Test2 is clicked
476fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
477 term.take_focus().unwrap();
478 term.reset_terminal();
479
480 for i in 0..50 {
481 term.append(&format!("{i}\n"));
482 }
483 assert_eq!(term.history_rows(), 100);
484
485 term.clear_history();
486 assert_eq!(term.history_use(), 0);
487
488 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
489 term.append("\nDone!\n");
490 term.set_text_attrib(Attrib::Normal);
491}
492
493//--------------------------------------------------------------------------------------
494/// Another set of tests that run when Test3 is clicked
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496 term.take_focus().unwrap();
497 term.reset_terminal();
498 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500 assert_eq!(term.history_use(), 0);
501 term.clear();
502 assert_eq!(term.cursor_row(), 0);
503 assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505 term.append("Test\ntext\na\nb\nc\nd");
506 assert_eq!(term.cursor_row(), 5);
507 let hist = term.history_use();
508 term.clear_screen_home(false);
509 assert_eq!(term.cursor_row(), 0);
510 assert_eq!(term.history_use(), hist); // History not changed
511
512 term.append("Test\ntext\na\nb\nc\nd\ne");
513 assert_eq!(term.cursor_row(), 6);
514 term.clear_screen_home(true);
515 assert_eq!(term.cursor_row(), 0);
516
517 term.append("Test\ntext\na\nb\nc\n");
518 assert_eq!(term.cursor_row(), 5);
519 term.clear_to_color(Color::DarkBlue);
520 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521 assert_eq!(term.text_bg_color(), Color::TransparentBg);
522 assert_eq!(term.cursor_row(), 0);
523
524 // Test cursor_home()
525 term.append("Test\n\n\n\n\n\n\n\n\n\n");
526 assert_eq!(term.cursor_row(), 10);
527 term.cursor_home();
528 assert_eq!(term.cursor_row(), 0);
529
530 // Test the widget color
531 assert_eq!(term.color(), Color::Black); // Default
532 term.set_color(Color::DarkGreen);
533 assert_eq!(term.color(), Color::DarkGreen);
534 term.set_color(Color::Black);
535 assert_eq!(term.color(), Color::Black);
536 term.append(
537 "This should be one line of white text on black, embedded into the top of a blue field.\n",
538 );
539
540 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541 term.set_output_translate(OutFlags::OFF);
542 assert_eq!(term.output_translate(), OutFlags::OFF);
543 term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547 term.append("\nDone!\n");
548 term.set_text_attrib(Attrib::Normal);
549}
550
551//--------------------------------------------------------------------------------------
552/// Another set of tests for the ring-buffer access methods
553/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555 let sel_len = term.selection_text_len();
556 let sel = term.selection_text();
557
558 term.take_focus().unwrap();
559 term.reset_terminal();
560 // Test the Utf8Char primitive
561 let uc = Utf8Char::new(b'Q');
562 let uc1 = uc.text_utf8();
563 assert_eq!(&uc1, b"Q");
564 assert_eq!(&uc.attrib(), &Attrib::Normal);
565 assert_eq!(
566 &uc.charflags(),
567 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568 );
569 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570 assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572 let ring_rows = term.ring_rows();
573
574 term.take_focus().unwrap();
575 term.clear_history();
576 assert_eq!(term.history_use(), 0);
577
578 // Subtract row numbers, modulo `rows`
579 fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580 match a - b {
581 n if n < 0 => n + rows,
582 n => n,
583 }
584 }
585 // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587 assert!(term.disp_srow() >= 0);
588 assert!(term.disp_erow() >= 0);
589 assert!(term.hist_srow() >= 0);
590 assert!(term.hist_erow() >= 0);
591 assert!(term.offset() >= 0);
592 assert!(term.disp_srow() <= ring_rows);
593 assert!(term.disp_erow() <= ring_rows);
594 assert!(term.hist_srow() <= ring_rows);
595 assert!(term.hist_erow() <= ring_rows);
596 assert!(term.offset() <= ring_rows);
597
598 assert_eq!(term.ring_srow(), 0);
599 assert_eq!(term.ring_erow(), ring_rows - 1);
600 assert_eq!(
601 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602 term.display_rows()
603 );
604 assert_eq!(
605 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606 term.history_rows()
607 );
608
609 assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610 assert_eq!(term.ring_srow(), 0);
611
612 /// Local function to read back all rows from the display into a long string.
613 /// Does not include scrollback history.
614 /// Trims trailing blanks on each line
615 fn read_disp(term: &Terminal) -> String {
616 let rows = term.display_rows();
617 let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618 for row in 0..rows {
619 let r = term.u8c_disp_row(row).trim();
620 // Iterate through a row, accumulating [u8]
621 for c in r.iter() {
622 // Note: Sometimes utf-8 length is > 1
623 text.extend_from_slice(c.text_utf8());
624 }
625 text.extend_from_slice(b"\n");
626 }
627 // Return the result as a string
628 std::str::from_utf8(&text).unwrap().to_string()
629 }
630
631 term.clear();
632 term.append("Top line ↑ (up-arrow)");
633 term.set_text_attrib(Attrib::Underline);
634 term.append(" ");
635 term.set_text_attrib(Attrib::Normal);
636 term.append(" \n");
637 let mut text_out = read_disp(term);
638 // Trim trailing empty lines
639 text_out = text_out.trim_end_matches(&"\n").to_string();
640 // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642 assert_eq!(text_out, "Top line ↑ (up-arrow) ");
643 let r = term.u8c_disp_row(0);
644 assert_eq!(r.col(0).text_utf8(), b"T");
645 assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646 assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647 let r = term.u8c_disp_row(1);
648 assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649 assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651 // Clear the screen again, then append test text, then read it back and compare
652 let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656 Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659 term.clear_history();
660 term.clear();
661 let bg_save = term.text_bg_color();
662 let fg_save = term.text_fg_color();
663 term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664 term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665 term.append(test_text);
666 term.set_text_bg_color(bg_save);
667 term.set_text_fg_color(fg_save);
668
669 let mut text_out = read_disp(term);
670 // Trim trailing empty lines
671 text_out = text_out.trim_end_matches(&"\n").to_string();
672 assert_eq!(test_text, text_out);
673
674 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676 assert_eq!(term.ring_srow(), 0);
677 assert_eq!(term.ring_erow(), ring_rows - 1);
678 assert_eq!(
679 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680 term.display_rows()
681 );
682 assert_eq!(
683 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684 term.history_rows()
685 );
686
687 term.append(&format!(
688 "\n\nScreen has {} rows of {} columns.\n",
689 term.display_rows(),
690 term.display_columns()
691 ));
692
693 term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
695
696//--------------------------------------------------------------------------------------
697/// Yet another set of tests for misc cursor functions and other stuff
698/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700 term.take_focus().unwrap();
701
702 // Test the attr_fg_color and attr_bg_color methods.
703 // Put a single character 'A' into the buffer and check it
704 term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705 term.set_text_bg_color(Color::TransparentBg);
706 term.set_text_fg_color(Color::XtermWhite);
707 term.append("A");
708 let r = &term.u8c_disp_row(0);
709 let uc = r.col(0);
710 assert_eq!(uc.text_utf8(), b"A");
711 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712 assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713 assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714 assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715 assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717 // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718 term.clear();
719 term.set_text_fg_color_xterm(XtermColor::White);
720 term.set_text_bg_color_xterm(XtermColor::Black);
721 assert_eq!(term.text_attrib(), Attrib::Normal);
722
723 assert!(term.ansi());
724 term.append("B\x1b[32mC\x1b[1mD\n");
725
726 let r = &term.u8c_disp_row(0);
727 let uc = r.col(0);
728 assert_eq!(uc.text_utf8(), b"B");
729 assert!(uc.is_char(b'B'));
730 assert!(!uc.is_char(b'A'));
731 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735 assert_eq!(
736 &uc.charflags(),
737 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738 );
739
740 let uc = r.col(1);
741 assert_eq!(uc.text_utf8(), b"C");
742 assert!(uc.is_char(b'C'));
743 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747 assert_eq!(
748 &uc.charflags(),
749 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750 );
751
752 let uc = r.col(2);
753 assert_eq!(uc.text_utf8(), b"D");
754 assert!(uc.is_char(b'D'));
755 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757 assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759 assert_eq!(
760 &uc.attr_fgcolor(Some(term)),
761 &Color::from_rgb(0x20, 0xf0, 0x20)
762 );
763 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764 assert_eq!(
765 &uc.charflags(),
766 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767 );
768
769 // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770 term.clear();
771 term.set_text_fg_color_xterm(XtermColor::White);
772 term.set_text_bg_color_xterm(XtermColor::Black);
773 term.set_text_attrib(Attrib::Normal);
774 assert_eq!(term.text_attrib(), Attrib::Normal);
775
776 assert!(term.ansi());
777 term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779 let r = &term.u8c_disp_row(0);
780 let uc = r.col(0);
781 assert_eq!(uc.text_utf8(), b"B");
782 assert!(uc.is_char(b'B'));
783 assert!(!uc.is_char(b'A'));
784 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788 assert_eq!(
789 &uc.charflags(),
790 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791 );
792
793 let uc = r.col(1);
794 assert_eq!(uc.text_utf8(), b"C");
795 assert!(uc.is_char(b'C'));
796 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800 assert_eq!(
801 &uc.charflags(),
802 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803 );
804
805 let uc = r.col(2);
806 assert_eq!(uc.text_utf8(), b"D");
807 assert!(uc.is_char(b'D'));
808 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812 assert_eq!(
813 &uc.charflags(),
814 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815 );
816
817 let uc = r.col(3);
818 assert_eq!(uc.text_utf8(), b"E");
819 assert!(uc.is_char(b'E'));
820 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822 assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823 assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824 assert_eq!(
825 &uc.charflags(),
826 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827 );
828
829 // Test some miscellaneous Utf8 constants
830 assert_eq!(uc.length(), 1);
831 assert_eq!(uc.max_utf8(), 4);
832 assert_eq!(uc.pwidth(), 9.0);
833 assert_eq!(uc.pwidth_int(), 9);
834
835 term.set_text_fg_color_xterm(XtermColor::White);
836 term.set_text_bg_color_xterm(XtermColor::Black);
837 term.clear();
838 term.set_text_attrib(Attrib::Normal);
839
840 // Mouse selection functions
841 term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842 term.clear_mouse_selection();
843 assert_eq!(term.get_selection(), None);
844
845 // Play with cursor position
846 term.append("0123456789\n"); // Set up test pattern
847 term.append("ABCDEFGHIJ\n");
848 term.append("abcdefghij\n");
849
850 term.set_cursor_row(1);
851 assert_eq!(term.cursor_row(), 1);
852 term.set_cursor_col(1);
853 assert_eq!(term.cursor_col(), 1);
854 assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856 term.append("----"); // Overwrites text at cursor and moves cursor forward
857 assert_eq!(term.cursor_row(), 1);
858 assert_eq!(term.cursor_col(), 5);
859 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860 term.set_cursor_col(1);
861 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863 term.cursor_up(1, false);
864 assert_eq!(term.cursor_row(), 0);
865 assert_eq!(term.cursor_col(), 1);
866 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868 // Hit top of screen, so nothing happens
869 term.cursor_up(1, false);
870 assert_eq!(term.cursor_row(), 0);
871 assert_eq!(term.cursor_col(), 1);
872 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874 // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875 term.cursor_up(1, true);
876 assert_eq!(term.cursor_row(), 0);
877 assert_eq!(term.cursor_col(), 1);
878 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880 // Go back down to the overwritten text
881 term.cursor_down(2, false);
882 assert_eq!(term.cursor_row(), 2);
883 assert_eq!(term.cursor_col(), 1);
884 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886 // Go right past the overwritten text
887 term.cursor_right(4, false);
888 assert_eq!(term.cursor_row(), 2);
889 assert_eq!(term.cursor_col(), 5);
890 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892 // Go left to the end of the overwritten text
893 term.cursor_left(1);
894 assert_eq!(term.cursor_row(), 2);
895 assert_eq!(term.cursor_col(), 4);
896 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898 // Scroll back down, removing the blank line at the top.
899 // Cursor stays in place, the text moves under it.
900 term.scroll(1);
901 assert_eq!(term.cursor_row(), 2);
902 assert_eq!(term.cursor_col(), 4);
903 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905 // Clear from here to end-of-line
906 term.clear_eol();
907 assert_eq!(term.cursor_row(), 2);
908 assert_eq!(term.cursor_col(), 4);
909 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911 // Now clear from here to start-of-line. Cursor does not move.
912 term.clear_sol();
913 assert_eq!(term.cursor_row(), 2);
914 assert_eq!(term.cursor_col(), 4);
915 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916 term.cursor_left(1);
917 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918 term.set_cursor_col(0);
919 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921 // Clear some lines
922 term.clear_line(1);
923 assert_eq!(term.cursor_row(), 2);
924 assert_eq!(term.cursor_col(), 0);
925 term.set_cursor_row(1);
926 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927 term.set_cursor_row(3);
928 term.clear_cur_line();
929 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930 assert_eq!(term.cursor_row(), 3);
931 assert_eq!(term.cursor_col(), 0);
932
933 term.append("Two lines above are intentionally left blank.\n");
934 assert_eq!(term.cursor_row(), 4);
935 assert_eq!(term.cursor_col(), 0);
936
937 // Set up the test pattern again, then play with insert/delete
938 term.append("0123456789\n");
939 term.append("ABCDEFGHIJ\n");
940 term.append("abcdefghij\n");
941 assert_eq!(term.cursor_row(), 7);
942
943 term.set_cursor_row(4);
944 term.set_cursor_col(4);
945 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947 term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949 term.cursor_right(5, false);
950 assert_eq!(term.cursor_col(), 9);
951 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953 // Insert two blank rows above cursor. Cursor stays put.
954 term.insert_rows(2);
955 assert_eq!(term.cursor_row(), 4);
956 assert_eq!(term.cursor_col(), 9);
957 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958 term.cursor_down(2, false); // Go down to find our text again
959 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961 // Go back to the beginning of the inserted 'x' characters and delete them.
962 term.cursor_left(5);
963 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964 term.delete_cur_chars(5);
965 assert_eq!(term.cursor_row(), 6);
966 assert_eq!(term.cursor_col(), 4);
967 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969 term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970 term.cursor_down(1, false);
971 term.cursor_left(2);
972 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974 term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975 assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976 term.cursor_up(1, false);
977 term.delete_rows(2); // Delete remains of test pattern
978
979 term.set_text_attrib(Attrib::Bold);
980 term.insert_char_eol('-', 3, 15, 20);
981 term.set_cursor_row(3);
982 term.set_cursor_col(15);
983 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984 assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986 term.set_text_attrib(Attrib::Italic);
987 term.append(" and all lines below");
988 term.set_text_attrib(Attrib::Normal);
989 term.cursor_down(1, false);
990
991 let mut hsb = term.hscrollbar();
992 let mut sb = term.scrollbar();
993 hsb.set_value(100.0);
994 assert_eq!(hsb.value(), 100.0);
995 sb.set_value(50.0);
996 assert_eq!(sb.value(), 50.0);
997 hsb.set_value(0.0);
998 assert_eq!(hsb.value(), 0.0);
999 sb.set_value(0.0);
1000 assert_eq!(sb.value(), 0.0);
1001}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
425//--------------------------------------------------------------------------------------
426/// More tests that run when the menu bar Test1 is clicked
427fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
428 term.take_focus().unwrap();
429 term.reset_terminal();
430 term.append("0123456789 0\n");
431 term.append("0123456789 1\n");
432 term.append("0123456789 2\n");
433 term.append("0123456789 3\n");
434 term.append("0123456789 4\n");
435 term.append("0123456789 5\n");
436 term.append("0123456789 6\n");
437 term.append("0123456789 7\n");
438 term.append("0123456789 8\n");
439 term.append("0123456789 9\n");
440 term.append("------------\n");
441
442 term.set_text_fg_color(Color::Green);
443 term.plot_char('A', 0, 0);
444 term.plot_char('B', 1, 1);
445 term.plot_char('C', 2, 2);
446 term.plot_char('D', 3, 3);
447 term.plot_char('E', 4, 4);
448 term.plot_char('F', 5, 5);
449 term.set_text_fg_color(Color::XtermWhite);
450
451 assert_eq!(term.cursor_row(), 11);
452 assert_eq!(term.cursor_col(), 0);
453
454 term.set_text_bg_color(Color::DarkBlue);
455 term.plot_char_utf8('b', 8, 1);
456 term.plot_char_utf8('↑', 9, 1);
457 term.plot_char_utf8('c', 8, 2);
458 term.plot_char_utf8('↑', 9, 2);
459 term.plot_char_utf8('d', 8, 3);
460 term.plot_char_utf8('↑', 9, 3);
461 term.plot_char_utf8('e', 8, 4);
462 term.plot_char_utf8('↑', 9, 4);
463 term.plot_char_utf8('f', 8, 5);
464 term.plot_char_utf8('↑', 9, 5);
465 term.plot_char_utf8('g', 8, 6);
466 term.plot_char_utf8('↑', 9, 6);
467 term.set_text_bg_color(Color::TransparentBg);
468
469 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
470 term.append("Done!\n");
471 term.set_text_attrib(Attrib::Normal);
472}
473
474//--------------------------------------------------------------------------------------
475/// More tests that run when the menu bar button Test2 is clicked
476fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
477 term.take_focus().unwrap();
478 term.reset_terminal();
479
480 for i in 0..50 {
481 term.append(&format!("{i}\n"));
482 }
483 assert_eq!(term.history_rows(), 100);
484
485 term.clear_history();
486 assert_eq!(term.history_use(), 0);
487
488 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
489 term.append("\nDone!\n");
490 term.set_text_attrib(Attrib::Normal);
491}
492
493//--------------------------------------------------------------------------------------
494/// Another set of tests that run when Test3 is clicked
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496 term.take_focus().unwrap();
497 term.reset_terminal();
498 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500 assert_eq!(term.history_use(), 0);
501 term.clear();
502 assert_eq!(term.cursor_row(), 0);
503 assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505 term.append("Test\ntext\na\nb\nc\nd");
506 assert_eq!(term.cursor_row(), 5);
507 let hist = term.history_use();
508 term.clear_screen_home(false);
509 assert_eq!(term.cursor_row(), 0);
510 assert_eq!(term.history_use(), hist); // History not changed
511
512 term.append("Test\ntext\na\nb\nc\nd\ne");
513 assert_eq!(term.cursor_row(), 6);
514 term.clear_screen_home(true);
515 assert_eq!(term.cursor_row(), 0);
516
517 term.append("Test\ntext\na\nb\nc\n");
518 assert_eq!(term.cursor_row(), 5);
519 term.clear_to_color(Color::DarkBlue);
520 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521 assert_eq!(term.text_bg_color(), Color::TransparentBg);
522 assert_eq!(term.cursor_row(), 0);
523
524 // Test cursor_home()
525 term.append("Test\n\n\n\n\n\n\n\n\n\n");
526 assert_eq!(term.cursor_row(), 10);
527 term.cursor_home();
528 assert_eq!(term.cursor_row(), 0);
529
530 // Test the widget color
531 assert_eq!(term.color(), Color::Black); // Default
532 term.set_color(Color::DarkGreen);
533 assert_eq!(term.color(), Color::DarkGreen);
534 term.set_color(Color::Black);
535 assert_eq!(term.color(), Color::Black);
536 term.append(
537 "This should be one line of white text on black, embedded into the top of a blue field.\n",
538 );
539
540 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541 term.set_output_translate(OutFlags::OFF);
542 assert_eq!(term.output_translate(), OutFlags::OFF);
543 term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547 term.append("\nDone!\n");
548 term.set_text_attrib(Attrib::Normal);
549}
550
551//--------------------------------------------------------------------------------------
552/// Another set of tests for the ring-buffer access methods
553/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555 let sel_len = term.selection_text_len();
556 let sel = term.selection_text();
557
558 term.take_focus().unwrap();
559 term.reset_terminal();
560 // Test the Utf8Char primitive
561 let uc = Utf8Char::new(b'Q');
562 let uc1 = uc.text_utf8();
563 assert_eq!(&uc1, b"Q");
564 assert_eq!(&uc.attrib(), &Attrib::Normal);
565 assert_eq!(
566 &uc.charflags(),
567 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568 );
569 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570 assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572 let ring_rows = term.ring_rows();
573
574 term.take_focus().unwrap();
575 term.clear_history();
576 assert_eq!(term.history_use(), 0);
577
578 // Subtract row numbers, modulo `rows`
579 fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580 match a - b {
581 n if n < 0 => n + rows,
582 n => n,
583 }
584 }
585 // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587 assert!(term.disp_srow() >= 0);
588 assert!(term.disp_erow() >= 0);
589 assert!(term.hist_srow() >= 0);
590 assert!(term.hist_erow() >= 0);
591 assert!(term.offset() >= 0);
592 assert!(term.disp_srow() <= ring_rows);
593 assert!(term.disp_erow() <= ring_rows);
594 assert!(term.hist_srow() <= ring_rows);
595 assert!(term.hist_erow() <= ring_rows);
596 assert!(term.offset() <= ring_rows);
597
598 assert_eq!(term.ring_srow(), 0);
599 assert_eq!(term.ring_erow(), ring_rows - 1);
600 assert_eq!(
601 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602 term.display_rows()
603 );
604 assert_eq!(
605 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606 term.history_rows()
607 );
608
609 assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610 assert_eq!(term.ring_srow(), 0);
611
612 /// Local function to read back all rows from the display into a long string.
613 /// Does not include scrollback history.
614 /// Trims trailing blanks on each line
615 fn read_disp(term: &Terminal) -> String {
616 let rows = term.display_rows();
617 let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618 for row in 0..rows {
619 let r = term.u8c_disp_row(row).trim();
620 // Iterate through a row, accumulating [u8]
621 for c in r.iter() {
622 // Note: Sometimes utf-8 length is > 1
623 text.extend_from_slice(c.text_utf8());
624 }
625 text.extend_from_slice(b"\n");
626 }
627 // Return the result as a string
628 std::str::from_utf8(&text).unwrap().to_string()
629 }
630
631 term.clear();
632 term.append("Top line ↑ (up-arrow)");
633 term.set_text_attrib(Attrib::Underline);
634 term.append(" ");
635 term.set_text_attrib(Attrib::Normal);
636 term.append(" \n");
637 let mut text_out = read_disp(term);
638 // Trim trailing empty lines
639 text_out = text_out.trim_end_matches(&"\n").to_string();
640 // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642 assert_eq!(text_out, "Top line ↑ (up-arrow) ");
643 let r = term.u8c_disp_row(0);
644 assert_eq!(r.col(0).text_utf8(), b"T");
645 assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646 assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647 let r = term.u8c_disp_row(1);
648 assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649 assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651 // Clear the screen again, then append test text, then read it back and compare
652 let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656 Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659 term.clear_history();
660 term.clear();
661 let bg_save = term.text_bg_color();
662 let fg_save = term.text_fg_color();
663 term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664 term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665 term.append(test_text);
666 term.set_text_bg_color(bg_save);
667 term.set_text_fg_color(fg_save);
668
669 let mut text_out = read_disp(term);
670 // Trim trailing empty lines
671 text_out = text_out.trim_end_matches(&"\n").to_string();
672 assert_eq!(test_text, text_out);
673
674 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676 assert_eq!(term.ring_srow(), 0);
677 assert_eq!(term.ring_erow(), ring_rows - 1);
678 assert_eq!(
679 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680 term.display_rows()
681 );
682 assert_eq!(
683 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684 term.history_rows()
685 );
686
687 term.append(&format!(
688 "\n\nScreen has {} rows of {} columns.\n",
689 term.display_rows(),
690 term.display_columns()
691 ));
692
693 term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
425//--------------------------------------------------------------------------------------
426/// More tests that run when the menu bar Test1 is clicked
427fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
428 term.take_focus().unwrap();
429 term.reset_terminal();
430 term.append("0123456789 0\n");
431 term.append("0123456789 1\n");
432 term.append("0123456789 2\n");
433 term.append("0123456789 3\n");
434 term.append("0123456789 4\n");
435 term.append("0123456789 5\n");
436 term.append("0123456789 6\n");
437 term.append("0123456789 7\n");
438 term.append("0123456789 8\n");
439 term.append("0123456789 9\n");
440 term.append("------------\n");
441
442 term.set_text_fg_color(Color::Green);
443 term.plot_char('A', 0, 0);
444 term.plot_char('B', 1, 1);
445 term.plot_char('C', 2, 2);
446 term.plot_char('D', 3, 3);
447 term.plot_char('E', 4, 4);
448 term.plot_char('F', 5, 5);
449 term.set_text_fg_color(Color::XtermWhite);
450
451 assert_eq!(term.cursor_row(), 11);
452 assert_eq!(term.cursor_col(), 0);
453
454 term.set_text_bg_color(Color::DarkBlue);
455 term.plot_char_utf8('b', 8, 1);
456 term.plot_char_utf8('↑', 9, 1);
457 term.plot_char_utf8('c', 8, 2);
458 term.plot_char_utf8('↑', 9, 2);
459 term.plot_char_utf8('d', 8, 3);
460 term.plot_char_utf8('↑', 9, 3);
461 term.plot_char_utf8('e', 8, 4);
462 term.plot_char_utf8('↑', 9, 4);
463 term.plot_char_utf8('f', 8, 5);
464 term.plot_char_utf8('↑', 9, 5);
465 term.plot_char_utf8('g', 8, 6);
466 term.plot_char_utf8('↑', 9, 6);
467 term.set_text_bg_color(Color::TransparentBg);
468
469 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
470 term.append("Done!\n");
471 term.set_text_attrib(Attrib::Normal);
472}
473
474//--------------------------------------------------------------------------------------
475/// More tests that run when the menu bar button Test2 is clicked
476fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
477 term.take_focus().unwrap();
478 term.reset_terminal();
479
480 for i in 0..50 {
481 term.append(&format!("{i}\n"));
482 }
483 assert_eq!(term.history_rows(), 100);
484
485 term.clear_history();
486 assert_eq!(term.history_use(), 0);
487
488 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
489 term.append("\nDone!\n");
490 term.set_text_attrib(Attrib::Normal);
491}
492
493//--------------------------------------------------------------------------------------
494/// Another set of tests that run when Test3 is clicked
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496 term.take_focus().unwrap();
497 term.reset_terminal();
498 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500 assert_eq!(term.history_use(), 0);
501 term.clear();
502 assert_eq!(term.cursor_row(), 0);
503 assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505 term.append("Test\ntext\na\nb\nc\nd");
506 assert_eq!(term.cursor_row(), 5);
507 let hist = term.history_use();
508 term.clear_screen_home(false);
509 assert_eq!(term.cursor_row(), 0);
510 assert_eq!(term.history_use(), hist); // History not changed
511
512 term.append("Test\ntext\na\nb\nc\nd\ne");
513 assert_eq!(term.cursor_row(), 6);
514 term.clear_screen_home(true);
515 assert_eq!(term.cursor_row(), 0);
516
517 term.append("Test\ntext\na\nb\nc\n");
518 assert_eq!(term.cursor_row(), 5);
519 term.clear_to_color(Color::DarkBlue);
520 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521 assert_eq!(term.text_bg_color(), Color::TransparentBg);
522 assert_eq!(term.cursor_row(), 0);
523
524 // Test cursor_home()
525 term.append("Test\n\n\n\n\n\n\n\n\n\n");
526 assert_eq!(term.cursor_row(), 10);
527 term.cursor_home();
528 assert_eq!(term.cursor_row(), 0);
529
530 // Test the widget color
531 assert_eq!(term.color(), Color::Black); // Default
532 term.set_color(Color::DarkGreen);
533 assert_eq!(term.color(), Color::DarkGreen);
534 term.set_color(Color::Black);
535 assert_eq!(term.color(), Color::Black);
536 term.append(
537 "This should be one line of white text on black, embedded into the top of a blue field.\n",
538 );
539
540 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541 term.set_output_translate(OutFlags::OFF);
542 assert_eq!(term.output_translate(), OutFlags::OFF);
543 term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547 term.append("\nDone!\n");
548 term.set_text_attrib(Attrib::Normal);
549}
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?
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700 term.take_focus().unwrap();
701
702 // Test the attr_fg_color and attr_bg_color methods.
703 // Put a single character 'A' into the buffer and check it
704 term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705 term.set_text_bg_color(Color::TransparentBg);
706 term.set_text_fg_color(Color::XtermWhite);
707 term.append("A");
708 let r = &term.u8c_disp_row(0);
709 let uc = r.col(0);
710 assert_eq!(uc.text_utf8(), b"A");
711 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712 assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713 assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714 assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715 assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717 // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718 term.clear();
719 term.set_text_fg_color_xterm(XtermColor::White);
720 term.set_text_bg_color_xterm(XtermColor::Black);
721 assert_eq!(term.text_attrib(), Attrib::Normal);
722
723 assert!(term.ansi());
724 term.append("B\x1b[32mC\x1b[1mD\n");
725
726 let r = &term.u8c_disp_row(0);
727 let uc = r.col(0);
728 assert_eq!(uc.text_utf8(), b"B");
729 assert!(uc.is_char(b'B'));
730 assert!(!uc.is_char(b'A'));
731 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735 assert_eq!(
736 &uc.charflags(),
737 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738 );
739
740 let uc = r.col(1);
741 assert_eq!(uc.text_utf8(), b"C");
742 assert!(uc.is_char(b'C'));
743 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747 assert_eq!(
748 &uc.charflags(),
749 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750 );
751
752 let uc = r.col(2);
753 assert_eq!(uc.text_utf8(), b"D");
754 assert!(uc.is_char(b'D'));
755 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757 assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759 assert_eq!(
760 &uc.attr_fgcolor(Some(term)),
761 &Color::from_rgb(0x20, 0xf0, 0x20)
762 );
763 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764 assert_eq!(
765 &uc.charflags(),
766 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767 );
768
769 // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770 term.clear();
771 term.set_text_fg_color_xterm(XtermColor::White);
772 term.set_text_bg_color_xterm(XtermColor::Black);
773 term.set_text_attrib(Attrib::Normal);
774 assert_eq!(term.text_attrib(), Attrib::Normal);
775
776 assert!(term.ansi());
777 term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779 let r = &term.u8c_disp_row(0);
780 let uc = r.col(0);
781 assert_eq!(uc.text_utf8(), b"B");
782 assert!(uc.is_char(b'B'));
783 assert!(!uc.is_char(b'A'));
784 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788 assert_eq!(
789 &uc.charflags(),
790 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791 );
792
793 let uc = r.col(1);
794 assert_eq!(uc.text_utf8(), b"C");
795 assert!(uc.is_char(b'C'));
796 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800 assert_eq!(
801 &uc.charflags(),
802 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803 );
804
805 let uc = r.col(2);
806 assert_eq!(uc.text_utf8(), b"D");
807 assert!(uc.is_char(b'D'));
808 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812 assert_eq!(
813 &uc.charflags(),
814 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815 );
816
817 let uc = r.col(3);
818 assert_eq!(uc.text_utf8(), b"E");
819 assert!(uc.is_char(b'E'));
820 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822 assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823 assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824 assert_eq!(
825 &uc.charflags(),
826 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827 );
828
829 // Test some miscellaneous Utf8 constants
830 assert_eq!(uc.length(), 1);
831 assert_eq!(uc.max_utf8(), 4);
832 assert_eq!(uc.pwidth(), 9.0);
833 assert_eq!(uc.pwidth_int(), 9);
834
835 term.set_text_fg_color_xterm(XtermColor::White);
836 term.set_text_bg_color_xterm(XtermColor::Black);
837 term.clear();
838 term.set_text_attrib(Attrib::Normal);
839
840 // Mouse selection functions
841 term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842 term.clear_mouse_selection();
843 assert_eq!(term.get_selection(), None);
844
845 // Play with cursor position
846 term.append("0123456789\n"); // Set up test pattern
847 term.append("ABCDEFGHIJ\n");
848 term.append("abcdefghij\n");
849
850 term.set_cursor_row(1);
851 assert_eq!(term.cursor_row(), 1);
852 term.set_cursor_col(1);
853 assert_eq!(term.cursor_col(), 1);
854 assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856 term.append("----"); // Overwrites text at cursor and moves cursor forward
857 assert_eq!(term.cursor_row(), 1);
858 assert_eq!(term.cursor_col(), 5);
859 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860 term.set_cursor_col(1);
861 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863 term.cursor_up(1, false);
864 assert_eq!(term.cursor_row(), 0);
865 assert_eq!(term.cursor_col(), 1);
866 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868 // Hit top of screen, so nothing happens
869 term.cursor_up(1, false);
870 assert_eq!(term.cursor_row(), 0);
871 assert_eq!(term.cursor_col(), 1);
872 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874 // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875 term.cursor_up(1, true);
876 assert_eq!(term.cursor_row(), 0);
877 assert_eq!(term.cursor_col(), 1);
878 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880 // Go back down to the overwritten text
881 term.cursor_down(2, false);
882 assert_eq!(term.cursor_row(), 2);
883 assert_eq!(term.cursor_col(), 1);
884 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886 // Go right past the overwritten text
887 term.cursor_right(4, false);
888 assert_eq!(term.cursor_row(), 2);
889 assert_eq!(term.cursor_col(), 5);
890 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892 // Go left to the end of the overwritten text
893 term.cursor_left(1);
894 assert_eq!(term.cursor_row(), 2);
895 assert_eq!(term.cursor_col(), 4);
896 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898 // Scroll back down, removing the blank line at the top.
899 // Cursor stays in place, the text moves under it.
900 term.scroll(1);
901 assert_eq!(term.cursor_row(), 2);
902 assert_eq!(term.cursor_col(), 4);
903 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905 // Clear from here to end-of-line
906 term.clear_eol();
907 assert_eq!(term.cursor_row(), 2);
908 assert_eq!(term.cursor_col(), 4);
909 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911 // Now clear from here to start-of-line. Cursor does not move.
912 term.clear_sol();
913 assert_eq!(term.cursor_row(), 2);
914 assert_eq!(term.cursor_col(), 4);
915 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916 term.cursor_left(1);
917 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918 term.set_cursor_col(0);
919 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921 // Clear some lines
922 term.clear_line(1);
923 assert_eq!(term.cursor_row(), 2);
924 assert_eq!(term.cursor_col(), 0);
925 term.set_cursor_row(1);
926 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927 term.set_cursor_row(3);
928 term.clear_cur_line();
929 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930 assert_eq!(term.cursor_row(), 3);
931 assert_eq!(term.cursor_col(), 0);
932
933 term.append("Two lines above are intentionally left blank.\n");
934 assert_eq!(term.cursor_row(), 4);
935 assert_eq!(term.cursor_col(), 0);
936
937 // Set up the test pattern again, then play with insert/delete
938 term.append("0123456789\n");
939 term.append("ABCDEFGHIJ\n");
940 term.append("abcdefghij\n");
941 assert_eq!(term.cursor_row(), 7);
942
943 term.set_cursor_row(4);
944 term.set_cursor_col(4);
945 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947 term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949 term.cursor_right(5, false);
950 assert_eq!(term.cursor_col(), 9);
951 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953 // Insert two blank rows above cursor. Cursor stays put.
954 term.insert_rows(2);
955 assert_eq!(term.cursor_row(), 4);
956 assert_eq!(term.cursor_col(), 9);
957 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958 term.cursor_down(2, false); // Go down to find our text again
959 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961 // Go back to the beginning of the inserted 'x' characters and delete them.
962 term.cursor_left(5);
963 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964 term.delete_cur_chars(5);
965 assert_eq!(term.cursor_row(), 6);
966 assert_eq!(term.cursor_col(), 4);
967 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969 term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970 term.cursor_down(1, false);
971 term.cursor_left(2);
972 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974 term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975 assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976 term.cursor_up(1, false);
977 term.delete_rows(2); // Delete remains of test pattern
978
979 term.set_text_attrib(Attrib::Bold);
980 term.insert_char_eol('-', 3, 15, 20);
981 term.set_cursor_row(3);
982 term.set_cursor_col(15);
983 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984 assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986 term.set_text_attrib(Attrib::Italic);
987 term.append(" and all lines below");
988 term.set_text_attrib(Attrib::Normal);
989 term.cursor_down(1, false);
990
991 let mut hsb = term.hscrollbar();
992 let mut sb = term.scrollbar();
993 hsb.set_value(100.0);
994 assert_eq!(hsb.value(), 100.0);
995 sb.set_value(50.0);
996 assert_eq!(sb.value(), 50.0);
997 hsb.set_value(0.0);
998 assert_eq!(hsb.value(), 0.0);
999 sb.set_value(0.0);
1000 assert_eq!(sb.value(), 0.0);
1001}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
425//--------------------------------------------------------------------------------------
426/// More tests that run when the menu bar Test1 is clicked
427fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
428 term.take_focus().unwrap();
429 term.reset_terminal();
430 term.append("0123456789 0\n");
431 term.append("0123456789 1\n");
432 term.append("0123456789 2\n");
433 term.append("0123456789 3\n");
434 term.append("0123456789 4\n");
435 term.append("0123456789 5\n");
436 term.append("0123456789 6\n");
437 term.append("0123456789 7\n");
438 term.append("0123456789 8\n");
439 term.append("0123456789 9\n");
440 term.append("------------\n");
441
442 term.set_text_fg_color(Color::Green);
443 term.plot_char('A', 0, 0);
444 term.plot_char('B', 1, 1);
445 term.plot_char('C', 2, 2);
446 term.plot_char('D', 3, 3);
447 term.plot_char('E', 4, 4);
448 term.plot_char('F', 5, 5);
449 term.set_text_fg_color(Color::XtermWhite);
450
451 assert_eq!(term.cursor_row(), 11);
452 assert_eq!(term.cursor_col(), 0);
453
454 term.set_text_bg_color(Color::DarkBlue);
455 term.plot_char_utf8('b', 8, 1);
456 term.plot_char_utf8('↑', 9, 1);
457 term.plot_char_utf8('c', 8, 2);
458 term.plot_char_utf8('↑', 9, 2);
459 term.plot_char_utf8('d', 8, 3);
460 term.plot_char_utf8('↑', 9, 3);
461 term.plot_char_utf8('e', 8, 4);
462 term.plot_char_utf8('↑', 9, 4);
463 term.plot_char_utf8('f', 8, 5);
464 term.plot_char_utf8('↑', 9, 5);
465 term.plot_char_utf8('g', 8, 6);
466 term.plot_char_utf8('↑', 9, 6);
467 term.set_text_bg_color(Color::TransparentBg);
468
469 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
470 term.append("Done!\n");
471 term.set_text_attrib(Attrib::Normal);
472}
473
474//--------------------------------------------------------------------------------------
475/// More tests that run when the menu bar button Test2 is clicked
476fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
477 term.take_focus().unwrap();
478 term.reset_terminal();
479
480 for i in 0..50 {
481 term.append(&format!("{i}\n"));
482 }
483 assert_eq!(term.history_rows(), 100);
484
485 term.clear_history();
486 assert_eq!(term.history_use(), 0);
487
488 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
489 term.append("\nDone!\n");
490 term.set_text_attrib(Attrib::Normal);
491}
492
493//--------------------------------------------------------------------------------------
494/// Another set of tests that run when Test3 is clicked
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496 term.take_focus().unwrap();
497 term.reset_terminal();
498 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500 assert_eq!(term.history_use(), 0);
501 term.clear();
502 assert_eq!(term.cursor_row(), 0);
503 assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505 term.append("Test\ntext\na\nb\nc\nd");
506 assert_eq!(term.cursor_row(), 5);
507 let hist = term.history_use();
508 term.clear_screen_home(false);
509 assert_eq!(term.cursor_row(), 0);
510 assert_eq!(term.history_use(), hist); // History not changed
511
512 term.append("Test\ntext\na\nb\nc\nd\ne");
513 assert_eq!(term.cursor_row(), 6);
514 term.clear_screen_home(true);
515 assert_eq!(term.cursor_row(), 0);
516
517 term.append("Test\ntext\na\nb\nc\n");
518 assert_eq!(term.cursor_row(), 5);
519 term.clear_to_color(Color::DarkBlue);
520 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521 assert_eq!(term.text_bg_color(), Color::TransparentBg);
522 assert_eq!(term.cursor_row(), 0);
523
524 // Test cursor_home()
525 term.append("Test\n\n\n\n\n\n\n\n\n\n");
526 assert_eq!(term.cursor_row(), 10);
527 term.cursor_home();
528 assert_eq!(term.cursor_row(), 0);
529
530 // Test the widget color
531 assert_eq!(term.color(), Color::Black); // Default
532 term.set_color(Color::DarkGreen);
533 assert_eq!(term.color(), Color::DarkGreen);
534 term.set_color(Color::Black);
535 assert_eq!(term.color(), Color::Black);
536 term.append(
537 "This should be one line of white text on black, embedded into the top of a blue field.\n",
538 );
539
540 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541 term.set_output_translate(OutFlags::OFF);
542 assert_eq!(term.output_translate(), OutFlags::OFF);
543 term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547 term.append("\nDone!\n");
548 term.set_text_attrib(Attrib::Normal);
549}
550
551//--------------------------------------------------------------------------------------
552/// Another set of tests for the ring-buffer access methods
553/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555 let sel_len = term.selection_text_len();
556 let sel = term.selection_text();
557
558 term.take_focus().unwrap();
559 term.reset_terminal();
560 // Test the Utf8Char primitive
561 let uc = Utf8Char::new(b'Q');
562 let uc1 = uc.text_utf8();
563 assert_eq!(&uc1, b"Q");
564 assert_eq!(&uc.attrib(), &Attrib::Normal);
565 assert_eq!(
566 &uc.charflags(),
567 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568 );
569 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570 assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572 let ring_rows = term.ring_rows();
573
574 term.take_focus().unwrap();
575 term.clear_history();
576 assert_eq!(term.history_use(), 0);
577
578 // Subtract row numbers, modulo `rows`
579 fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580 match a - b {
581 n if n < 0 => n + rows,
582 n => n,
583 }
584 }
585 // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587 assert!(term.disp_srow() >= 0);
588 assert!(term.disp_erow() >= 0);
589 assert!(term.hist_srow() >= 0);
590 assert!(term.hist_erow() >= 0);
591 assert!(term.offset() >= 0);
592 assert!(term.disp_srow() <= ring_rows);
593 assert!(term.disp_erow() <= ring_rows);
594 assert!(term.hist_srow() <= ring_rows);
595 assert!(term.hist_erow() <= ring_rows);
596 assert!(term.offset() <= ring_rows);
597
598 assert_eq!(term.ring_srow(), 0);
599 assert_eq!(term.ring_erow(), ring_rows - 1);
600 assert_eq!(
601 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602 term.display_rows()
603 );
604 assert_eq!(
605 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606 term.history_rows()
607 );
608
609 assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610 assert_eq!(term.ring_srow(), 0);
611
612 /// Local function to read back all rows from the display into a long string.
613 /// Does not include scrollback history.
614 /// Trims trailing blanks on each line
615 fn read_disp(term: &Terminal) -> String {
616 let rows = term.display_rows();
617 let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618 for row in 0..rows {
619 let r = term.u8c_disp_row(row).trim();
620 // Iterate through a row, accumulating [u8]
621 for c in r.iter() {
622 // Note: Sometimes utf-8 length is > 1
623 text.extend_from_slice(c.text_utf8());
624 }
625 text.extend_from_slice(b"\n");
626 }
627 // Return the result as a string
628 std::str::from_utf8(&text).unwrap().to_string()
629 }
630
631 term.clear();
632 term.append("Top line ↑ (up-arrow)");
633 term.set_text_attrib(Attrib::Underline);
634 term.append(" ");
635 term.set_text_attrib(Attrib::Normal);
636 term.append(" \n");
637 let mut text_out = read_disp(term);
638 // Trim trailing empty lines
639 text_out = text_out.trim_end_matches(&"\n").to_string();
640 // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642 assert_eq!(text_out, "Top line ↑ (up-arrow) ");
643 let r = term.u8c_disp_row(0);
644 assert_eq!(r.col(0).text_utf8(), b"T");
645 assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646 assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647 let r = term.u8c_disp_row(1);
648 assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649 assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651 // Clear the screen again, then append test text, then read it back and compare
652 let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656 Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659 term.clear_history();
660 term.clear();
661 let bg_save = term.text_bg_color();
662 let fg_save = term.text_fg_color();
663 term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664 term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665 term.append(test_text);
666 term.set_text_bg_color(bg_save);
667 term.set_text_fg_color(fg_save);
668
669 let mut text_out = read_disp(term);
670 // Trim trailing empty lines
671 text_out = text_out.trim_end_matches(&"\n").to_string();
672 assert_eq!(test_text, text_out);
673
674 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676 assert_eq!(term.ring_srow(), 0);
677 assert_eq!(term.ring_erow(), ring_rows - 1);
678 assert_eq!(
679 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680 term.display_rows()
681 );
682 assert_eq!(
683 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684 term.history_rows()
685 );
686
687 term.append(&format!(
688 "\n\nScreen has {} rows of {} columns.\n",
689 term.display_rows(),
690 term.display_columns()
691 ));
692
693 term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
695
696//--------------------------------------------------------------------------------------
697/// Yet another set of tests for misc cursor functions and other stuff
698/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700 term.take_focus().unwrap();
701
702 // Test the attr_fg_color and attr_bg_color methods.
703 // Put a single character 'A' into the buffer and check it
704 term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705 term.set_text_bg_color(Color::TransparentBg);
706 term.set_text_fg_color(Color::XtermWhite);
707 term.append("A");
708 let r = &term.u8c_disp_row(0);
709 let uc = r.col(0);
710 assert_eq!(uc.text_utf8(), b"A");
711 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712 assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713 assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714 assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715 assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717 // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718 term.clear();
719 term.set_text_fg_color_xterm(XtermColor::White);
720 term.set_text_bg_color_xterm(XtermColor::Black);
721 assert_eq!(term.text_attrib(), Attrib::Normal);
722
723 assert!(term.ansi());
724 term.append("B\x1b[32mC\x1b[1mD\n");
725
726 let r = &term.u8c_disp_row(0);
727 let uc = r.col(0);
728 assert_eq!(uc.text_utf8(), b"B");
729 assert!(uc.is_char(b'B'));
730 assert!(!uc.is_char(b'A'));
731 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735 assert_eq!(
736 &uc.charflags(),
737 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738 );
739
740 let uc = r.col(1);
741 assert_eq!(uc.text_utf8(), b"C");
742 assert!(uc.is_char(b'C'));
743 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747 assert_eq!(
748 &uc.charflags(),
749 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750 );
751
752 let uc = r.col(2);
753 assert_eq!(uc.text_utf8(), b"D");
754 assert!(uc.is_char(b'D'));
755 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757 assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759 assert_eq!(
760 &uc.attr_fgcolor(Some(term)),
761 &Color::from_rgb(0x20, 0xf0, 0x20)
762 );
763 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764 assert_eq!(
765 &uc.charflags(),
766 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767 );
768
769 // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770 term.clear();
771 term.set_text_fg_color_xterm(XtermColor::White);
772 term.set_text_bg_color_xterm(XtermColor::Black);
773 term.set_text_attrib(Attrib::Normal);
774 assert_eq!(term.text_attrib(), Attrib::Normal);
775
776 assert!(term.ansi());
777 term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779 let r = &term.u8c_disp_row(0);
780 let uc = r.col(0);
781 assert_eq!(uc.text_utf8(), b"B");
782 assert!(uc.is_char(b'B'));
783 assert!(!uc.is_char(b'A'));
784 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788 assert_eq!(
789 &uc.charflags(),
790 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791 );
792
793 let uc = r.col(1);
794 assert_eq!(uc.text_utf8(), b"C");
795 assert!(uc.is_char(b'C'));
796 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800 assert_eq!(
801 &uc.charflags(),
802 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803 );
804
805 let uc = r.col(2);
806 assert_eq!(uc.text_utf8(), b"D");
807 assert!(uc.is_char(b'D'));
808 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812 assert_eq!(
813 &uc.charflags(),
814 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815 );
816
817 let uc = r.col(3);
818 assert_eq!(uc.text_utf8(), b"E");
819 assert!(uc.is_char(b'E'));
820 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822 assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823 assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824 assert_eq!(
825 &uc.charflags(),
826 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827 );
828
829 // Test some miscellaneous Utf8 constants
830 assert_eq!(uc.length(), 1);
831 assert_eq!(uc.max_utf8(), 4);
832 assert_eq!(uc.pwidth(), 9.0);
833 assert_eq!(uc.pwidth_int(), 9);
834
835 term.set_text_fg_color_xterm(XtermColor::White);
836 term.set_text_bg_color_xterm(XtermColor::Black);
837 term.clear();
838 term.set_text_attrib(Attrib::Normal);
839
840 // Mouse selection functions
841 term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842 term.clear_mouse_selection();
843 assert_eq!(term.get_selection(), None);
844
845 // Play with cursor position
846 term.append("0123456789\n"); // Set up test pattern
847 term.append("ABCDEFGHIJ\n");
848 term.append("abcdefghij\n");
849
850 term.set_cursor_row(1);
851 assert_eq!(term.cursor_row(), 1);
852 term.set_cursor_col(1);
853 assert_eq!(term.cursor_col(), 1);
854 assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856 term.append("----"); // Overwrites text at cursor and moves cursor forward
857 assert_eq!(term.cursor_row(), 1);
858 assert_eq!(term.cursor_col(), 5);
859 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860 term.set_cursor_col(1);
861 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863 term.cursor_up(1, false);
864 assert_eq!(term.cursor_row(), 0);
865 assert_eq!(term.cursor_col(), 1);
866 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868 // Hit top of screen, so nothing happens
869 term.cursor_up(1, false);
870 assert_eq!(term.cursor_row(), 0);
871 assert_eq!(term.cursor_col(), 1);
872 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874 // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875 term.cursor_up(1, true);
876 assert_eq!(term.cursor_row(), 0);
877 assert_eq!(term.cursor_col(), 1);
878 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880 // Go back down to the overwritten text
881 term.cursor_down(2, false);
882 assert_eq!(term.cursor_row(), 2);
883 assert_eq!(term.cursor_col(), 1);
884 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886 // Go right past the overwritten text
887 term.cursor_right(4, false);
888 assert_eq!(term.cursor_row(), 2);
889 assert_eq!(term.cursor_col(), 5);
890 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892 // Go left to the end of the overwritten text
893 term.cursor_left(1);
894 assert_eq!(term.cursor_row(), 2);
895 assert_eq!(term.cursor_col(), 4);
896 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898 // Scroll back down, removing the blank line at the top.
899 // Cursor stays in place, the text moves under it.
900 term.scroll(1);
901 assert_eq!(term.cursor_row(), 2);
902 assert_eq!(term.cursor_col(), 4);
903 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905 // Clear from here to end-of-line
906 term.clear_eol();
907 assert_eq!(term.cursor_row(), 2);
908 assert_eq!(term.cursor_col(), 4);
909 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911 // Now clear from here to start-of-line. Cursor does not move.
912 term.clear_sol();
913 assert_eq!(term.cursor_row(), 2);
914 assert_eq!(term.cursor_col(), 4);
915 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916 term.cursor_left(1);
917 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918 term.set_cursor_col(0);
919 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921 // Clear some lines
922 term.clear_line(1);
923 assert_eq!(term.cursor_row(), 2);
924 assert_eq!(term.cursor_col(), 0);
925 term.set_cursor_row(1);
926 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927 term.set_cursor_row(3);
928 term.clear_cur_line();
929 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930 assert_eq!(term.cursor_row(), 3);
931 assert_eq!(term.cursor_col(), 0);
932
933 term.append("Two lines above are intentionally left blank.\n");
934 assert_eq!(term.cursor_row(), 4);
935 assert_eq!(term.cursor_col(), 0);
936
937 // Set up the test pattern again, then play with insert/delete
938 term.append("0123456789\n");
939 term.append("ABCDEFGHIJ\n");
940 term.append("abcdefghij\n");
941 assert_eq!(term.cursor_row(), 7);
942
943 term.set_cursor_row(4);
944 term.set_cursor_col(4);
945 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947 term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949 term.cursor_right(5, false);
950 assert_eq!(term.cursor_col(), 9);
951 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953 // Insert two blank rows above cursor. Cursor stays put.
954 term.insert_rows(2);
955 assert_eq!(term.cursor_row(), 4);
956 assert_eq!(term.cursor_col(), 9);
957 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958 term.cursor_down(2, false); // Go down to find our text again
959 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961 // Go back to the beginning of the inserted 'x' characters and delete them.
962 term.cursor_left(5);
963 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964 term.delete_cur_chars(5);
965 assert_eq!(term.cursor_row(), 6);
966 assert_eq!(term.cursor_col(), 4);
967 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969 term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970 term.cursor_down(1, false);
971 term.cursor_left(2);
972 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974 term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975 assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976 term.cursor_up(1, false);
977 term.delete_rows(2); // Delete remains of test pattern
978
979 term.set_text_attrib(Attrib::Bold);
980 term.insert_char_eol('-', 3, 15, 20);
981 term.set_cursor_row(3);
982 term.set_cursor_col(15);
983 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984 assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986 term.set_text_attrib(Attrib::Italic);
987 term.append(" and all lines below");
988 term.set_text_attrib(Attrib::Normal);
989 term.cursor_down(1, false);
990
991 let mut hsb = term.hscrollbar();
992 let mut sb = term.scrollbar();
993 hsb.set_value(100.0);
994 assert_eq!(hsb.value(), 100.0);
995 sb.set_value(50.0);
996 assert_eq!(sb.value(), 50.0);
997 hsb.set_value(0.0);
998 assert_eq!(hsb.value(), 0.0);
999 sb.set_value(0.0);
1000 assert_eq!(sb.value(), 0.0);
1001}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
425//--------------------------------------------------------------------------------------
426/// More tests that run when the menu bar Test1 is clicked
427fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
428 term.take_focus().unwrap();
429 term.reset_terminal();
430 term.append("0123456789 0\n");
431 term.append("0123456789 1\n");
432 term.append("0123456789 2\n");
433 term.append("0123456789 3\n");
434 term.append("0123456789 4\n");
435 term.append("0123456789 5\n");
436 term.append("0123456789 6\n");
437 term.append("0123456789 7\n");
438 term.append("0123456789 8\n");
439 term.append("0123456789 9\n");
440 term.append("------------\n");
441
442 term.set_text_fg_color(Color::Green);
443 term.plot_char('A', 0, 0);
444 term.plot_char('B', 1, 1);
445 term.plot_char('C', 2, 2);
446 term.plot_char('D', 3, 3);
447 term.plot_char('E', 4, 4);
448 term.plot_char('F', 5, 5);
449 term.set_text_fg_color(Color::XtermWhite);
450
451 assert_eq!(term.cursor_row(), 11);
452 assert_eq!(term.cursor_col(), 0);
453
454 term.set_text_bg_color(Color::DarkBlue);
455 term.plot_char_utf8('b', 8, 1);
456 term.plot_char_utf8('↑', 9, 1);
457 term.plot_char_utf8('c', 8, 2);
458 term.plot_char_utf8('↑', 9, 2);
459 term.plot_char_utf8('d', 8, 3);
460 term.plot_char_utf8('↑', 9, 3);
461 term.plot_char_utf8('e', 8, 4);
462 term.plot_char_utf8('↑', 9, 4);
463 term.plot_char_utf8('f', 8, 5);
464 term.plot_char_utf8('↑', 9, 5);
465 term.plot_char_utf8('g', 8, 6);
466 term.plot_char_utf8('↑', 9, 6);
467 term.set_text_bg_color(Color::TransparentBg);
468
469 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
470 term.append("Done!\n");
471 term.set_text_attrib(Attrib::Normal);
472}
473
474//--------------------------------------------------------------------------------------
475/// More tests that run when the menu bar button Test2 is clicked
476fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
477 term.take_focus().unwrap();
478 term.reset_terminal();
479
480 for i in 0..50 {
481 term.append(&format!("{i}\n"));
482 }
483 assert_eq!(term.history_rows(), 100);
484
485 term.clear_history();
486 assert_eq!(term.history_use(), 0);
487
488 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
489 term.append("\nDone!\n");
490 term.set_text_attrib(Attrib::Normal);
491}
492
493//--------------------------------------------------------------------------------------
494/// Another set of tests that run when Test3 is clicked
495fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
496 term.take_focus().unwrap();
497 term.reset_terminal();
498 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
499
500 assert_eq!(term.history_use(), 0);
501 term.clear();
502 assert_eq!(term.cursor_row(), 0);
503 assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
504
505 term.append("Test\ntext\na\nb\nc\nd");
506 assert_eq!(term.cursor_row(), 5);
507 let hist = term.history_use();
508 term.clear_screen_home(false);
509 assert_eq!(term.cursor_row(), 0);
510 assert_eq!(term.history_use(), hist); // History not changed
511
512 term.append("Test\ntext\na\nb\nc\nd\ne");
513 assert_eq!(term.cursor_row(), 6);
514 term.clear_screen_home(true);
515 assert_eq!(term.cursor_row(), 0);
516
517 term.append("Test\ntext\na\nb\nc\n");
518 assert_eq!(term.cursor_row(), 5);
519 term.clear_to_color(Color::DarkBlue);
520 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
521 assert_eq!(term.text_bg_color(), Color::TransparentBg);
522 assert_eq!(term.cursor_row(), 0);
523
524 // Test cursor_home()
525 term.append("Test\n\n\n\n\n\n\n\n\n\n");
526 assert_eq!(term.cursor_row(), 10);
527 term.cursor_home();
528 assert_eq!(term.cursor_row(), 0);
529
530 // Test the widget color
531 assert_eq!(term.color(), Color::Black); // Default
532 term.set_color(Color::DarkGreen);
533 assert_eq!(term.color(), Color::DarkGreen);
534 term.set_color(Color::Black);
535 assert_eq!(term.color(), Color::Black);
536 term.append(
537 "This should be one line of white text on black, embedded into the top of a blue field.\n",
538 );
539
540 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
541 term.set_output_translate(OutFlags::OFF);
542 assert_eq!(term.output_translate(), OutFlags::OFF);
543 term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
544 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
545
546 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
547 term.append("\nDone!\n");
548 term.set_text_attrib(Attrib::Normal);
549}
550
551//--------------------------------------------------------------------------------------
552/// Another set of tests for the ring-buffer access methods
553/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555 let sel_len = term.selection_text_len();
556 let sel = term.selection_text();
557
558 term.take_focus().unwrap();
559 term.reset_terminal();
560 // Test the Utf8Char primitive
561 let uc = Utf8Char::new(b'Q');
562 let uc1 = uc.text_utf8();
563 assert_eq!(&uc1, b"Q");
564 assert_eq!(&uc.attrib(), &Attrib::Normal);
565 assert_eq!(
566 &uc.charflags(),
567 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568 );
569 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570 assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572 let ring_rows = term.ring_rows();
573
574 term.take_focus().unwrap();
575 term.clear_history();
576 assert_eq!(term.history_use(), 0);
577
578 // Subtract row numbers, modulo `rows`
579 fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580 match a - b {
581 n if n < 0 => n + rows,
582 n => n,
583 }
584 }
585 // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587 assert!(term.disp_srow() >= 0);
588 assert!(term.disp_erow() >= 0);
589 assert!(term.hist_srow() >= 0);
590 assert!(term.hist_erow() >= 0);
591 assert!(term.offset() >= 0);
592 assert!(term.disp_srow() <= ring_rows);
593 assert!(term.disp_erow() <= ring_rows);
594 assert!(term.hist_srow() <= ring_rows);
595 assert!(term.hist_erow() <= ring_rows);
596 assert!(term.offset() <= ring_rows);
597
598 assert_eq!(term.ring_srow(), 0);
599 assert_eq!(term.ring_erow(), ring_rows - 1);
600 assert_eq!(
601 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602 term.display_rows()
603 );
604 assert_eq!(
605 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606 term.history_rows()
607 );
608
609 assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610 assert_eq!(term.ring_srow(), 0);
611
612 /// Local function to read back all rows from the display into a long string.
613 /// Does not include scrollback history.
614 /// Trims trailing blanks on each line
615 fn read_disp(term: &Terminal) -> String {
616 let rows = term.display_rows();
617 let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618 for row in 0..rows {
619 let r = term.u8c_disp_row(row).trim();
620 // Iterate through a row, accumulating [u8]
621 for c in r.iter() {
622 // Note: Sometimes utf-8 length is > 1
623 text.extend_from_slice(c.text_utf8());
624 }
625 text.extend_from_slice(b"\n");
626 }
627 // Return the result as a string
628 std::str::from_utf8(&text).unwrap().to_string()
629 }
630
631 term.clear();
632 term.append("Top line ↑ (up-arrow)");
633 term.set_text_attrib(Attrib::Underline);
634 term.append(" ");
635 term.set_text_attrib(Attrib::Normal);
636 term.append(" \n");
637 let mut text_out = read_disp(term);
638 // Trim trailing empty lines
639 text_out = text_out.trim_end_matches(&"\n").to_string();
640 // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642 assert_eq!(text_out, "Top line ↑ (up-arrow) ");
643 let r = term.u8c_disp_row(0);
644 assert_eq!(r.col(0).text_utf8(), b"T");
645 assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646 assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647 let r = term.u8c_disp_row(1);
648 assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649 assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651 // Clear the screen again, then append test text, then read it back and compare
652 let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656 Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659 term.clear_history();
660 term.clear();
661 let bg_save = term.text_bg_color();
662 let fg_save = term.text_fg_color();
663 term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664 term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665 term.append(test_text);
666 term.set_text_bg_color(bg_save);
667 term.set_text_fg_color(fg_save);
668
669 let mut text_out = read_disp(term);
670 // Trim trailing empty lines
671 text_out = text_out.trim_end_matches(&"\n").to_string();
672 assert_eq!(test_text, text_out);
673
674 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676 assert_eq!(term.ring_srow(), 0);
677 assert_eq!(term.ring_erow(), ring_rows - 1);
678 assert_eq!(
679 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680 term.display_rows()
681 );
682 assert_eq!(
683 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684 term.history_rows()
685 );
686
687 term.append(&format!(
688 "\n\nScreen has {} rows of {} columns.\n",
689 term.display_rows(),
690 term.display_columns()
691 ));
692
693 term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
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?
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700 term.take_focus().unwrap();
701
702 // Test the attr_fg_color and attr_bg_color methods.
703 // Put a single character 'A' into the buffer and check it
704 term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705 term.set_text_bg_color(Color::TransparentBg);
706 term.set_text_fg_color(Color::XtermWhite);
707 term.append("A");
708 let r = &term.u8c_disp_row(0);
709 let uc = r.col(0);
710 assert_eq!(uc.text_utf8(), b"A");
711 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712 assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713 assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714 assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715 assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717 // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718 term.clear();
719 term.set_text_fg_color_xterm(XtermColor::White);
720 term.set_text_bg_color_xterm(XtermColor::Black);
721 assert_eq!(term.text_attrib(), Attrib::Normal);
722
723 assert!(term.ansi());
724 term.append("B\x1b[32mC\x1b[1mD\n");
725
726 let r = &term.u8c_disp_row(0);
727 let uc = r.col(0);
728 assert_eq!(uc.text_utf8(), b"B");
729 assert!(uc.is_char(b'B'));
730 assert!(!uc.is_char(b'A'));
731 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735 assert_eq!(
736 &uc.charflags(),
737 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738 );
739
740 let uc = r.col(1);
741 assert_eq!(uc.text_utf8(), b"C");
742 assert!(uc.is_char(b'C'));
743 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747 assert_eq!(
748 &uc.charflags(),
749 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750 );
751
752 let uc = r.col(2);
753 assert_eq!(uc.text_utf8(), b"D");
754 assert!(uc.is_char(b'D'));
755 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757 assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759 assert_eq!(
760 &uc.attr_fgcolor(Some(term)),
761 &Color::from_rgb(0x20, 0xf0, 0x20)
762 );
763 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764 assert_eq!(
765 &uc.charflags(),
766 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767 );
768
769 // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770 term.clear();
771 term.set_text_fg_color_xterm(XtermColor::White);
772 term.set_text_bg_color_xterm(XtermColor::Black);
773 term.set_text_attrib(Attrib::Normal);
774 assert_eq!(term.text_attrib(), Attrib::Normal);
775
776 assert!(term.ansi());
777 term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779 let r = &term.u8c_disp_row(0);
780 let uc = r.col(0);
781 assert_eq!(uc.text_utf8(), b"B");
782 assert!(uc.is_char(b'B'));
783 assert!(!uc.is_char(b'A'));
784 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788 assert_eq!(
789 &uc.charflags(),
790 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791 );
792
793 let uc = r.col(1);
794 assert_eq!(uc.text_utf8(), b"C");
795 assert!(uc.is_char(b'C'));
796 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800 assert_eq!(
801 &uc.charflags(),
802 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803 );
804
805 let uc = r.col(2);
806 assert_eq!(uc.text_utf8(), b"D");
807 assert!(uc.is_char(b'D'));
808 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812 assert_eq!(
813 &uc.charflags(),
814 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815 );
816
817 let uc = r.col(3);
818 assert_eq!(uc.text_utf8(), b"E");
819 assert!(uc.is_char(b'E'));
820 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822 assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823 assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824 assert_eq!(
825 &uc.charflags(),
826 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827 );
828
829 // Test some miscellaneous Utf8 constants
830 assert_eq!(uc.length(), 1);
831 assert_eq!(uc.max_utf8(), 4);
832 assert_eq!(uc.pwidth(), 9.0);
833 assert_eq!(uc.pwidth_int(), 9);
834
835 term.set_text_fg_color_xterm(XtermColor::White);
836 term.set_text_bg_color_xterm(XtermColor::Black);
837 term.clear();
838 term.set_text_attrib(Attrib::Normal);
839
840 // Mouse selection functions
841 term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842 term.clear_mouse_selection();
843 assert_eq!(term.get_selection(), None);
844
845 // Play with cursor position
846 term.append("0123456789\n"); // Set up test pattern
847 term.append("ABCDEFGHIJ\n");
848 term.append("abcdefghij\n");
849
850 term.set_cursor_row(1);
851 assert_eq!(term.cursor_row(), 1);
852 term.set_cursor_col(1);
853 assert_eq!(term.cursor_col(), 1);
854 assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856 term.append("----"); // Overwrites text at cursor and moves cursor forward
857 assert_eq!(term.cursor_row(), 1);
858 assert_eq!(term.cursor_col(), 5);
859 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860 term.set_cursor_col(1);
861 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863 term.cursor_up(1, false);
864 assert_eq!(term.cursor_row(), 0);
865 assert_eq!(term.cursor_col(), 1);
866 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868 // Hit top of screen, so nothing happens
869 term.cursor_up(1, false);
870 assert_eq!(term.cursor_row(), 0);
871 assert_eq!(term.cursor_col(), 1);
872 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874 // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875 term.cursor_up(1, true);
876 assert_eq!(term.cursor_row(), 0);
877 assert_eq!(term.cursor_col(), 1);
878 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880 // Go back down to the overwritten text
881 term.cursor_down(2, false);
882 assert_eq!(term.cursor_row(), 2);
883 assert_eq!(term.cursor_col(), 1);
884 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886 // Go right past the overwritten text
887 term.cursor_right(4, false);
888 assert_eq!(term.cursor_row(), 2);
889 assert_eq!(term.cursor_col(), 5);
890 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892 // Go left to the end of the overwritten text
893 term.cursor_left(1);
894 assert_eq!(term.cursor_row(), 2);
895 assert_eq!(term.cursor_col(), 4);
896 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898 // Scroll back down, removing the blank line at the top.
899 // Cursor stays in place, the text moves under it.
900 term.scroll(1);
901 assert_eq!(term.cursor_row(), 2);
902 assert_eq!(term.cursor_col(), 4);
903 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905 // Clear from here to end-of-line
906 term.clear_eol();
907 assert_eq!(term.cursor_row(), 2);
908 assert_eq!(term.cursor_col(), 4);
909 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911 // Now clear from here to start-of-line. Cursor does not move.
912 term.clear_sol();
913 assert_eq!(term.cursor_row(), 2);
914 assert_eq!(term.cursor_col(), 4);
915 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916 term.cursor_left(1);
917 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918 term.set_cursor_col(0);
919 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921 // Clear some lines
922 term.clear_line(1);
923 assert_eq!(term.cursor_row(), 2);
924 assert_eq!(term.cursor_col(), 0);
925 term.set_cursor_row(1);
926 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927 term.set_cursor_row(3);
928 term.clear_cur_line();
929 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930 assert_eq!(term.cursor_row(), 3);
931 assert_eq!(term.cursor_col(), 0);
932
933 term.append("Two lines above are intentionally left blank.\n");
934 assert_eq!(term.cursor_row(), 4);
935 assert_eq!(term.cursor_col(), 0);
936
937 // Set up the test pattern again, then play with insert/delete
938 term.append("0123456789\n");
939 term.append("ABCDEFGHIJ\n");
940 term.append("abcdefghij\n");
941 assert_eq!(term.cursor_row(), 7);
942
943 term.set_cursor_row(4);
944 term.set_cursor_col(4);
945 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947 term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949 term.cursor_right(5, false);
950 assert_eq!(term.cursor_col(), 9);
951 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953 // Insert two blank rows above cursor. Cursor stays put.
954 term.insert_rows(2);
955 assert_eq!(term.cursor_row(), 4);
956 assert_eq!(term.cursor_col(), 9);
957 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958 term.cursor_down(2, false); // Go down to find our text again
959 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961 // Go back to the beginning of the inserted 'x' characters and delete them.
962 term.cursor_left(5);
963 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964 term.delete_cur_chars(5);
965 assert_eq!(term.cursor_row(), 6);
966 assert_eq!(term.cursor_col(), 4);
967 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969 term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970 term.cursor_down(1, false);
971 term.cursor_left(2);
972 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974 term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975 assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976 term.cursor_up(1, false);
977 term.delete_rows(2); // Delete remains of test pattern
978
979 term.set_text_attrib(Attrib::Bold);
980 term.insert_char_eol('-', 3, 15, 20);
981 term.set_cursor_row(3);
982 term.set_cursor_col(15);
983 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984 assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986 term.set_text_attrib(Attrib::Italic);
987 term.append(" and all lines below");
988 term.set_text_attrib(Attrib::Normal);
989 term.cursor_down(1, false);
990
991 let mut hsb = term.hscrollbar();
992 let mut sb = term.scrollbar();
993 hsb.set_value(100.0);
994 assert_eq!(hsb.value(), 100.0);
995 sb.set_value(50.0);
996 assert_eq!(sb.value(), 50.0);
997 hsb.set_value(0.0);
998 assert_eq!(hsb.value(), 0.0);
999 sb.set_value(0.0);
1000 assert_eq!(sb.value(), 0.0);
1001}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
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!(
115 term.text(true),
116 "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
117 );
118
119 // Testing ansi() and set_ansi() methods
120 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
121 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
122 term.set_ansi(false);
123 assert!(!term.ansi());
124 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
125 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
126 term.append_u8(b"Appending u8 array\n");
127 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
128 term.set_ansi(true); // Restore ANSI state
129
130 // Play with the horizontal scrollbar
131 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
132 term.set_hscrollbar_style(ScrollbarStyle::ON);
133 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
134
135 // Test show_unknown() as incidental part of testing append methods
136 term.set_show_unknown(true);
137 assert!(term.show_unknown());
138 term.append_ascii(
139 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
140 );
141 term.set_show_unknown(false);
142 assert!(!term.show_unknown());
143
144 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
145 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
146
147 let r = term.cursor_row();
148 assert_eq!(term.cursor_col(), 0);
149 term.append(&format!("Testing cursor row/col {r}"));
150 assert_eq!(term.cursor_col(), 24);
151 assert_eq!(term.cursor_row(), r);
152
153 // Test cursor color methods
154 assert_eq!(
155 term.cursor_bg_color(),
156 Color::XtermGreen,
157 "Default cursor bg at startup"
158 );
159 term.set_cursor_bg_color(Color::Red);
160 assert_eq!(term.cursor_bg_color(), Color::Red);
161 term.set_cursor_fg_color(Color::Blue);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::Blue);
164 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
165 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
166 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
167 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
168
169 // The default display_rows() will derive from the window size
170 let dr = term.display_rows();
171 let height = term.h();
172 assert_eq!(height, term.h());
173 assert!(dr > 20, "Default display_rows at startup");
174 term.resize(term.x(), term.y(), term.w(), height * 2);
175 assert_eq!(term.h(), height * 2);
176 assert_eq!(height * 2, term.h());
177 assert!(term.display_rows() > dr);
178 term.resize(term.x(), term.y(), term.w(), height);
179
180 // The default display_columns() will derive from the window size
181 let dc = term.display_columns();
182 assert!(dc > 80, "Default display_rows at startup");
183 term.set_display_columns(200);
184 assert_eq!(term.display_columns(), 200);
185 term.append("\n 1 2 3 4 5 6 7 8 9");
186 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
187 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
188 term.set_display_columns(90);
189 assert_eq!(term.display_columns(), 90);
190 term.set_display_columns(dc); // Set back to default
191 assert_eq!(term.display_columns(), dc);
192
193 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
194 term.set_history_rows(50);
195 assert_eq!(term.history_rows(), 50);
196 term.set_history_rows(100); // Set back to default
197 assert_eq!(term.history_rows(), 100);
198
199 let hu = term.history_use();
200 term.append(&format!(
201 "history_use = {hu} (it's not clear what this means)\n"
202 ));
203 // assert_eq!(term.history_use(), hu+1);
204
205 term.append(&format!(
206 "margins = b:{} l:{} r:{} t{}\n",
207 term.margin_bottom(),
208 term.margin_left(),
209 term.margin_right(),
210 term.margin_top()
211 ));
212 assert_eq!(term.margin_bottom(), 3);
213 assert_eq!(term.margin_left(), 3);
214 assert_eq!(term.margin_right(), 3);
215 assert_eq!(term.margin_top(), 3);
216
217 term.set_margin_bottom(5);
218 term.set_margin_left(10);
219 term.set_margin_right(15);
220 term.set_margin_top(20);
221 assert_eq!(term.margin_bottom(), 5);
222 assert_eq!(term.margin_left(), 10);
223 assert_eq!(term.margin_right(), 15);
224 assert_eq!(term.margin_top(), 20);
225
226 term.append("Single character: '");
227 term.print_char('X');
228 term.append("', single UTF-8 character: '");
229 term.print_char_utf8('↑');
230 term.append("'\n");
231
232 let rr = term.redraw_rate();
233 assert_eq!(rr, 0.1, "Default redraw rate at startup");
234 term.append(&format!("Redraw rate {rr}\n"));
235 term.set_redraw_rate(1.0);
236 assert_eq!(term.redraw_rate(), 1.0);
237 term.set_redraw_rate(rr);
238 assert_eq!(term.redraw_rate(), rr);
239
240 let rs = term.redraw_style();
241 term.append(&format!("Redraw style {rs:?}\n"));
242 assert_eq!(
243 rs,
244 RedrawStyle::RateLimited,
245 "Default redraw style at startup"
246 );
247 term.set_redraw_style(RedrawStyle::NoRedraw);
248 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
249 term.set_redraw_style(rs);
250 assert_eq!(term.redraw_style(), rs);
251
252 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
253 assert_eq!(
254 RedrawStyle::NoRedraw.bits(),
255 0x0000,
256 "RedrawStyle enum values have been reassigned"
257 );
258 assert_eq!(
259 RedrawStyle::RateLimited.bits(),
260 0x0001,
261 "RedrawStyle enum values have been reassigned"
262 );
263 assert_eq!(
264 RedrawStyle::PerWrite.bits(),
265 0x0002,
266 "RedrawStyle enum values have been reassigned"
267 );
268
269 let sb = term.scrollbar();
270 let hsb = term.hscrollbar();
271 // Both vertical and horizontal scrollbars are at zero
272 assert_eq!(sb.value(), 0.0);
273 assert_eq!(hsb.value(), 0.0);
274 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
275
276 term.append(&format!(
277 "Scrollbar actual size {}\n",
278 term.scrollbar_actual_size()
279 ));
280 assert_eq!(term.scrollbar_actual_size(), 16);
281 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
282 assert_eq!(
283 term.scrollbar_size(),
284 0,
285 "Default scrollbar size at startup"
286 );
287 term.set_scrollbar_size(40);
288 assert_eq!(term.scrollbar_size(), 40);
289 assert_eq!(term.scrollbar_actual_size(), 40);
290 term.append(&format!(
291 "Scrollbar actual size {}\n",
292 term.scrollbar_actual_size()
293 ));
294 term.set_scrollbar_size(0); // Restore default
295 assert_eq!(term.scrollbar_size(), 0);
296 assert_eq!(term.scrollbar_actual_size(), 16);
297
298 let sfc = term.selection_fg_color();
299 let sbc = term.selection_bg_color();
300 assert_eq!(sfc, Color::Black);
301 assert_eq!(sbc, Color::White);
302 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
303 term.set_selection_fg_color(Color::Green);
304 term.set_selection_bg_color(Color::DarkBlue);
305 assert_eq!(term.selection_fg_color(), Color::Green);
306 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
307 term.set_selection_fg_color(sfc);
308 term.set_selection_bg_color(sbc);
309 assert_eq!(term.selection_fg_color(), Color::Black);
310 assert_eq!(term.selection_bg_color(), Color::White);
311
312 let tfcd = term.text_fg_color_default();
313 let tbcd = term.text_bg_color_default();
314 assert_eq!(tfcd, Color::XtermWhite);
315 assert_eq!(tbcd, Color::TransparentBg);
316 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
317 term.set_text_fg_color_default(Color::Green);
318 term.set_text_bg_color_default(Color::DarkBlue);
319 assert_eq!(term.text_fg_color_default(), Color::Green);
320 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
321 term.set_text_fg_color_default(tfcd);
322 term.set_text_bg_color_default(tbcd);
323 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
324 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
325
326 let tfc = term.text_fg_color();
327 let tbc = term.text_bg_color();
328 assert_eq!(tfc, Color::XtermWhite);
329 assert_eq!(tbc, Color::TransparentBg);
330 term.append(&format!("Text colors: {sfc} {sbc}\n"));
331 term.set_text_fg_color(Color::Green);
332 term.set_text_bg_color(Color::DarkBlue);
333 assert_eq!(term.text_fg_color(), Color::Green);
334 assert_eq!(term.text_bg_color(), Color::DarkBlue);
335 term.set_text_fg_color(tfc);
336 term.set_text_bg_color(tbc);
337 assert_eq!(term.text_fg_color(), Color::XtermWhite);
338 assert_eq!(term.text_bg_color(), Color::TransparentBg);
339
340 let tf = term.text_font();
341 term.append(&format!("Text font: {tf:?}\n"));
342 assert_eq!(tf, Font::Courier);
343 term.set_text_font(Font::Screen);
344 assert_eq!(term.text_font(), Font::Screen);
345 term.set_text_font(tf);
346 assert_eq!(term.text_font(), Font::Courier);
347
348 let ts = term.text_size();
349 let r = term.h_to_row(100);
350 let c = term.w_to_col(100);
351 term.append(&format!(
352 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
353 ));
354 assert_eq!(ts, 14);
355 term.set_text_size(30);
356 assert_eq!(term.text_size(), 30);
357 term.append(&format!(
358 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
359 term.text_size(),
360 term.h_to_row(100),
361 term.w_to_col(100)
362 ));
363 term.set_text_size(ts);
364 assert_eq!(term.text_size(), ts);
365 term.append(&format!(
366 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
367 term.text_size(),
368 term.h_to_row(100),
369 term.w_to_col(100)
370 ));
371
372 // Keyboard handler
373 term.handle({
374 move |term, e| {
375 match e {
376 fltk::enums::Event::KeyDown
377 if fltk::app::event_key() == fltk::enums::Key::Escape =>
378 {
379 // false to let FLTK handle ESC. true to hide ESC
380 false
381 }
382
383 fltk::enums::Event::KeyDown
384 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
385 {
386 // We handle control keystroke
387 let k = fltk::app::event_text().unwrap();
388 term.append_utf8(&k);
389 true
390 }
391
392 fltk::enums::Event::KeyDown
393 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
394 {
395 // We handle normal printable keystroke
396 let k = fltk::app::event_text().unwrap();
397 term.take_focus().unwrap();
398 term.append(&k);
399 true
400 }
401
402 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
403 // We can do this, or else ignore them (return false)
404 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
405 // term.redraw();
406 // true
407 // }
408 _ => false, // Let FLTK handle everything else
409 }
410 }
411 });
412
413 let attr_save = term.text_attrib();
414 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
415 term.append("\nStartup tests complete. Keyboard is live.\n");
416 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
417 term.set_text_attrib(attr_save);
418 assert_eq!(term.text_attrib(), attr_save);
419 term.redraw();
420 }
421 });
422
423 app.run().unwrap();
424}
Sourcepub fn selection_text(&self) -> Option<String>
pub fn selection_text(&self) -> Option<String>
Return text selection (for copy()/paste()
operations)
Examples found in repository?
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555 let sel_len = term.selection_text_len();
556 let sel = term.selection_text();
557
558 term.take_focus().unwrap();
559 term.reset_terminal();
560 // Test the Utf8Char primitive
561 let uc = Utf8Char::new(b'Q');
562 let uc1 = uc.text_utf8();
563 assert_eq!(&uc1, b"Q");
564 assert_eq!(&uc.attrib(), &Attrib::Normal);
565 assert_eq!(
566 &uc.charflags(),
567 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568 );
569 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570 assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572 let ring_rows = term.ring_rows();
573
574 term.take_focus().unwrap();
575 term.clear_history();
576 assert_eq!(term.history_use(), 0);
577
578 // Subtract row numbers, modulo `rows`
579 fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580 match a - b {
581 n if n < 0 => n + rows,
582 n => n,
583 }
584 }
585 // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587 assert!(term.disp_srow() >= 0);
588 assert!(term.disp_erow() >= 0);
589 assert!(term.hist_srow() >= 0);
590 assert!(term.hist_erow() >= 0);
591 assert!(term.offset() >= 0);
592 assert!(term.disp_srow() <= ring_rows);
593 assert!(term.disp_erow() <= ring_rows);
594 assert!(term.hist_srow() <= ring_rows);
595 assert!(term.hist_erow() <= ring_rows);
596 assert!(term.offset() <= ring_rows);
597
598 assert_eq!(term.ring_srow(), 0);
599 assert_eq!(term.ring_erow(), ring_rows - 1);
600 assert_eq!(
601 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602 term.display_rows()
603 );
604 assert_eq!(
605 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606 term.history_rows()
607 );
608
609 assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610 assert_eq!(term.ring_srow(), 0);
611
612 /// Local function to read back all rows from the display into a long string.
613 /// Does not include scrollback history.
614 /// Trims trailing blanks on each line
615 fn read_disp(term: &Terminal) -> String {
616 let rows = term.display_rows();
617 let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618 for row in 0..rows {
619 let r = term.u8c_disp_row(row).trim();
620 // Iterate through a row, accumulating [u8]
621 for c in r.iter() {
622 // Note: Sometimes utf-8 length is > 1
623 text.extend_from_slice(c.text_utf8());
624 }
625 text.extend_from_slice(b"\n");
626 }
627 // Return the result as a string
628 std::str::from_utf8(&text).unwrap().to_string()
629 }
630
631 term.clear();
632 term.append("Top line ↑ (up-arrow)");
633 term.set_text_attrib(Attrib::Underline);
634 term.append(" ");
635 term.set_text_attrib(Attrib::Normal);
636 term.append(" \n");
637 let mut text_out = read_disp(term);
638 // Trim trailing empty lines
639 text_out = text_out.trim_end_matches(&"\n").to_string();
640 // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642 assert_eq!(text_out, "Top line ↑ (up-arrow) ");
643 let r = term.u8c_disp_row(0);
644 assert_eq!(r.col(0).text_utf8(), b"T");
645 assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646 assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647 let r = term.u8c_disp_row(1);
648 assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649 assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651 // Clear the screen again, then append test text, then read it back and compare
652 let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656 Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659 term.clear_history();
660 term.clear();
661 let bg_save = term.text_bg_color();
662 let fg_save = term.text_fg_color();
663 term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664 term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665 term.append(test_text);
666 term.set_text_bg_color(bg_save);
667 term.set_text_fg_color(fg_save);
668
669 let mut text_out = read_disp(term);
670 // Trim trailing empty lines
671 text_out = text_out.trim_end_matches(&"\n").to_string();
672 assert_eq!(test_text, text_out);
673
674 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676 assert_eq!(term.ring_srow(), 0);
677 assert_eq!(term.ring_erow(), ring_rows - 1);
678 assert_eq!(
679 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680 term.display_rows()
681 );
682 assert_eq!(
683 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684 term.history_rows()
685 );
686
687 term.append(&format!(
688 "\n\nScreen has {} rows of {} columns.\n",
689 term.display_rows(),
690 term.display_columns()
691 ));
692
693 term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
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?
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555 let sel_len = term.selection_text_len();
556 let sel = term.selection_text();
557
558 term.take_focus().unwrap();
559 term.reset_terminal();
560 // Test the Utf8Char primitive
561 let uc = Utf8Char::new(b'Q');
562 let uc1 = uc.text_utf8();
563 assert_eq!(&uc1, b"Q");
564 assert_eq!(&uc.attrib(), &Attrib::Normal);
565 assert_eq!(
566 &uc.charflags(),
567 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568 );
569 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570 assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572 let ring_rows = term.ring_rows();
573
574 term.take_focus().unwrap();
575 term.clear_history();
576 assert_eq!(term.history_use(), 0);
577
578 // Subtract row numbers, modulo `rows`
579 fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580 match a - b {
581 n if n < 0 => n + rows,
582 n => n,
583 }
584 }
585 // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587 assert!(term.disp_srow() >= 0);
588 assert!(term.disp_erow() >= 0);
589 assert!(term.hist_srow() >= 0);
590 assert!(term.hist_erow() >= 0);
591 assert!(term.offset() >= 0);
592 assert!(term.disp_srow() <= ring_rows);
593 assert!(term.disp_erow() <= ring_rows);
594 assert!(term.hist_srow() <= ring_rows);
595 assert!(term.hist_erow() <= ring_rows);
596 assert!(term.offset() <= ring_rows);
597
598 assert_eq!(term.ring_srow(), 0);
599 assert_eq!(term.ring_erow(), ring_rows - 1);
600 assert_eq!(
601 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602 term.display_rows()
603 );
604 assert_eq!(
605 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606 term.history_rows()
607 );
608
609 assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610 assert_eq!(term.ring_srow(), 0);
611
612 /// Local function to read back all rows from the display into a long string.
613 /// Does not include scrollback history.
614 /// Trims trailing blanks on each line
615 fn read_disp(term: &Terminal) -> String {
616 let rows = term.display_rows();
617 let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618 for row in 0..rows {
619 let r = term.u8c_disp_row(row).trim();
620 // Iterate through a row, accumulating [u8]
621 for c in r.iter() {
622 // Note: Sometimes utf-8 length is > 1
623 text.extend_from_slice(c.text_utf8());
624 }
625 text.extend_from_slice(b"\n");
626 }
627 // Return the result as a string
628 std::str::from_utf8(&text).unwrap().to_string()
629 }
630
631 term.clear();
632 term.append("Top line ↑ (up-arrow)");
633 term.set_text_attrib(Attrib::Underline);
634 term.append(" ");
635 term.set_text_attrib(Attrib::Normal);
636 term.append(" \n");
637 let mut text_out = read_disp(term);
638 // Trim trailing empty lines
639 text_out = text_out.trim_end_matches(&"\n").to_string();
640 // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642 assert_eq!(text_out, "Top line ↑ (up-arrow) ");
643 let r = term.u8c_disp_row(0);
644 assert_eq!(r.col(0).text_utf8(), b"T");
645 assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646 assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647 let r = term.u8c_disp_row(1);
648 assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649 assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651 // Clear the screen again, then append test text, then read it back and compare
652 let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656 Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659 term.clear_history();
660 term.clear();
661 let bg_save = term.text_bg_color();
662 let fg_save = term.text_fg_color();
663 term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664 term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665 term.append(test_text);
666 term.set_text_bg_color(bg_save);
667 term.set_text_fg_color(fg_save);
668
669 let mut text_out = read_disp(term);
670 // Trim trailing empty lines
671 text_out = text_out.trim_end_matches(&"\n").to_string();
672 assert_eq!(test_text, text_out);
673
674 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676 assert_eq!(term.ring_srow(), 0);
677 assert_eq!(term.ring_erow(), ring_rows - 1);
678 assert_eq!(
679 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680 term.display_rows()
681 );
682 assert_eq!(
683 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684 term.history_rows()
685 );
686
687 term.append(&format!(
688 "\n\nScreen has {} rows of {} columns.\n",
689 term.display_rows(),
690 term.display_columns()
691 ));
692
693 term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
Sourcepub fn disp_erow(&self) -> i32
pub fn disp_erow(&self) -> i32
Return the ending row# in the display area.
Examples found in repository?
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555 let sel_len = term.selection_text_len();
556 let sel = term.selection_text();
557
558 term.take_focus().unwrap();
559 term.reset_terminal();
560 // Test the Utf8Char primitive
561 let uc = Utf8Char::new(b'Q');
562 let uc1 = uc.text_utf8();
563 assert_eq!(&uc1, b"Q");
564 assert_eq!(&uc.attrib(), &Attrib::Normal);
565 assert_eq!(
566 &uc.charflags(),
567 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568 );
569 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570 assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572 let ring_rows = term.ring_rows();
573
574 term.take_focus().unwrap();
575 term.clear_history();
576 assert_eq!(term.history_use(), 0);
577
578 // Subtract row numbers, modulo `rows`
579 fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580 match a - b {
581 n if n < 0 => n + rows,
582 n => n,
583 }
584 }
585 // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587 assert!(term.disp_srow() >= 0);
588 assert!(term.disp_erow() >= 0);
589 assert!(term.hist_srow() >= 0);
590 assert!(term.hist_erow() >= 0);
591 assert!(term.offset() >= 0);
592 assert!(term.disp_srow() <= ring_rows);
593 assert!(term.disp_erow() <= ring_rows);
594 assert!(term.hist_srow() <= ring_rows);
595 assert!(term.hist_erow() <= ring_rows);
596 assert!(term.offset() <= ring_rows);
597
598 assert_eq!(term.ring_srow(), 0);
599 assert_eq!(term.ring_erow(), ring_rows - 1);
600 assert_eq!(
601 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602 term.display_rows()
603 );
604 assert_eq!(
605 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606 term.history_rows()
607 );
608
609 assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610 assert_eq!(term.ring_srow(), 0);
611
612 /// Local function to read back all rows from the display into a long string.
613 /// Does not include scrollback history.
614 /// Trims trailing blanks on each line
615 fn read_disp(term: &Terminal) -> String {
616 let rows = term.display_rows();
617 let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618 for row in 0..rows {
619 let r = term.u8c_disp_row(row).trim();
620 // Iterate through a row, accumulating [u8]
621 for c in r.iter() {
622 // Note: Sometimes utf-8 length is > 1
623 text.extend_from_slice(c.text_utf8());
624 }
625 text.extend_from_slice(b"\n");
626 }
627 // Return the result as a string
628 std::str::from_utf8(&text).unwrap().to_string()
629 }
630
631 term.clear();
632 term.append("Top line ↑ (up-arrow)");
633 term.set_text_attrib(Attrib::Underline);
634 term.append(" ");
635 term.set_text_attrib(Attrib::Normal);
636 term.append(" \n");
637 let mut text_out = read_disp(term);
638 // Trim trailing empty lines
639 text_out = text_out.trim_end_matches(&"\n").to_string();
640 // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642 assert_eq!(text_out, "Top line ↑ (up-arrow) ");
643 let r = term.u8c_disp_row(0);
644 assert_eq!(r.col(0).text_utf8(), b"T");
645 assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646 assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647 let r = term.u8c_disp_row(1);
648 assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649 assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651 // Clear the screen again, then append test text, then read it back and compare
652 let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656 Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659 term.clear_history();
660 term.clear();
661 let bg_save = term.text_bg_color();
662 let fg_save = term.text_fg_color();
663 term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664 term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665 term.append(test_text);
666 term.set_text_bg_color(bg_save);
667 term.set_text_fg_color(fg_save);
668
669 let mut text_out = read_disp(term);
670 // Trim trailing empty lines
671 text_out = text_out.trim_end_matches(&"\n").to_string();
672 assert_eq!(test_text, text_out);
673
674 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676 assert_eq!(term.ring_srow(), 0);
677 assert_eq!(term.ring_erow(), ring_rows - 1);
678 assert_eq!(
679 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680 term.display_rows()
681 );
682 assert_eq!(
683 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684 term.history_rows()
685 );
686
687 term.append(&format!(
688 "\n\nScreen has {} rows of {} columns.\n",
689 term.display_rows(),
690 term.display_columns()
691 ));
692
693 term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
Sourcepub fn disp_srow(&self) -> i32
pub fn disp_srow(&self) -> i32
Return the starting row# in the display area.
Examples found in repository?
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555 let sel_len = term.selection_text_len();
556 let sel = term.selection_text();
557
558 term.take_focus().unwrap();
559 term.reset_terminal();
560 // Test the Utf8Char primitive
561 let uc = Utf8Char::new(b'Q');
562 let uc1 = uc.text_utf8();
563 assert_eq!(&uc1, b"Q");
564 assert_eq!(&uc.attrib(), &Attrib::Normal);
565 assert_eq!(
566 &uc.charflags(),
567 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568 );
569 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570 assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572 let ring_rows = term.ring_rows();
573
574 term.take_focus().unwrap();
575 term.clear_history();
576 assert_eq!(term.history_use(), 0);
577
578 // Subtract row numbers, modulo `rows`
579 fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580 match a - b {
581 n if n < 0 => n + rows,
582 n => n,
583 }
584 }
585 // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587 assert!(term.disp_srow() >= 0);
588 assert!(term.disp_erow() >= 0);
589 assert!(term.hist_srow() >= 0);
590 assert!(term.hist_erow() >= 0);
591 assert!(term.offset() >= 0);
592 assert!(term.disp_srow() <= ring_rows);
593 assert!(term.disp_erow() <= ring_rows);
594 assert!(term.hist_srow() <= ring_rows);
595 assert!(term.hist_erow() <= ring_rows);
596 assert!(term.offset() <= ring_rows);
597
598 assert_eq!(term.ring_srow(), 0);
599 assert_eq!(term.ring_erow(), ring_rows - 1);
600 assert_eq!(
601 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602 term.display_rows()
603 );
604 assert_eq!(
605 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606 term.history_rows()
607 );
608
609 assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610 assert_eq!(term.ring_srow(), 0);
611
612 /// Local function to read back all rows from the display into a long string.
613 /// Does not include scrollback history.
614 /// Trims trailing blanks on each line
615 fn read_disp(term: &Terminal) -> String {
616 let rows = term.display_rows();
617 let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618 for row in 0..rows {
619 let r = term.u8c_disp_row(row).trim();
620 // Iterate through a row, accumulating [u8]
621 for c in r.iter() {
622 // Note: Sometimes utf-8 length is > 1
623 text.extend_from_slice(c.text_utf8());
624 }
625 text.extend_from_slice(b"\n");
626 }
627 // Return the result as a string
628 std::str::from_utf8(&text).unwrap().to_string()
629 }
630
631 term.clear();
632 term.append("Top line ↑ (up-arrow)");
633 term.set_text_attrib(Attrib::Underline);
634 term.append(" ");
635 term.set_text_attrib(Attrib::Normal);
636 term.append(" \n");
637 let mut text_out = read_disp(term);
638 // Trim trailing empty lines
639 text_out = text_out.trim_end_matches(&"\n").to_string();
640 // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642 assert_eq!(text_out, "Top line ↑ (up-arrow) ");
643 let r = term.u8c_disp_row(0);
644 assert_eq!(r.col(0).text_utf8(), b"T");
645 assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646 assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647 let r = term.u8c_disp_row(1);
648 assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649 assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651 // Clear the screen again, then append test text, then read it back and compare
652 let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656 Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659 term.clear_history();
660 term.clear();
661 let bg_save = term.text_bg_color();
662 let fg_save = term.text_fg_color();
663 term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664 term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665 term.append(test_text);
666 term.set_text_bg_color(bg_save);
667 term.set_text_fg_color(fg_save);
668
669 let mut text_out = read_disp(term);
670 // Trim trailing empty lines
671 text_out = text_out.trim_end_matches(&"\n").to_string();
672 assert_eq!(test_text, text_out);
673
674 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676 assert_eq!(term.ring_srow(), 0);
677 assert_eq!(term.ring_erow(), ring_rows - 1);
678 assert_eq!(
679 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680 term.display_rows()
681 );
682 assert_eq!(
683 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684 term.history_rows()
685 );
686
687 term.append(&format!(
688 "\n\nScreen has {} rows of {} columns.\n",
689 term.display_rows(),
690 term.display_columns()
691 ));
692
693 term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
Sourcepub fn hist_erow(&self) -> i32
pub fn hist_erow(&self) -> i32
Return the ending row# of the scrollback history.
Examples found in repository?
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555 let sel_len = term.selection_text_len();
556 let sel = term.selection_text();
557
558 term.take_focus().unwrap();
559 term.reset_terminal();
560 // Test the Utf8Char primitive
561 let uc = Utf8Char::new(b'Q');
562 let uc1 = uc.text_utf8();
563 assert_eq!(&uc1, b"Q");
564 assert_eq!(&uc.attrib(), &Attrib::Normal);
565 assert_eq!(
566 &uc.charflags(),
567 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568 );
569 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570 assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572 let ring_rows = term.ring_rows();
573
574 term.take_focus().unwrap();
575 term.clear_history();
576 assert_eq!(term.history_use(), 0);
577
578 // Subtract row numbers, modulo `rows`
579 fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580 match a - b {
581 n if n < 0 => n + rows,
582 n => n,
583 }
584 }
585 // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587 assert!(term.disp_srow() >= 0);
588 assert!(term.disp_erow() >= 0);
589 assert!(term.hist_srow() >= 0);
590 assert!(term.hist_erow() >= 0);
591 assert!(term.offset() >= 0);
592 assert!(term.disp_srow() <= ring_rows);
593 assert!(term.disp_erow() <= ring_rows);
594 assert!(term.hist_srow() <= ring_rows);
595 assert!(term.hist_erow() <= ring_rows);
596 assert!(term.offset() <= ring_rows);
597
598 assert_eq!(term.ring_srow(), 0);
599 assert_eq!(term.ring_erow(), ring_rows - 1);
600 assert_eq!(
601 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602 term.display_rows()
603 );
604 assert_eq!(
605 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606 term.history_rows()
607 );
608
609 assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610 assert_eq!(term.ring_srow(), 0);
611
612 /// Local function to read back all rows from the display into a long string.
613 /// Does not include scrollback history.
614 /// Trims trailing blanks on each line
615 fn read_disp(term: &Terminal) -> String {
616 let rows = term.display_rows();
617 let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618 for row in 0..rows {
619 let r = term.u8c_disp_row(row).trim();
620 // Iterate through a row, accumulating [u8]
621 for c in r.iter() {
622 // Note: Sometimes utf-8 length is > 1
623 text.extend_from_slice(c.text_utf8());
624 }
625 text.extend_from_slice(b"\n");
626 }
627 // Return the result as a string
628 std::str::from_utf8(&text).unwrap().to_string()
629 }
630
631 term.clear();
632 term.append("Top line ↑ (up-arrow)");
633 term.set_text_attrib(Attrib::Underline);
634 term.append(" ");
635 term.set_text_attrib(Attrib::Normal);
636 term.append(" \n");
637 let mut text_out = read_disp(term);
638 // Trim trailing empty lines
639 text_out = text_out.trim_end_matches(&"\n").to_string();
640 // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642 assert_eq!(text_out, "Top line ↑ (up-arrow) ");
643 let r = term.u8c_disp_row(0);
644 assert_eq!(r.col(0).text_utf8(), b"T");
645 assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646 assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647 let r = term.u8c_disp_row(1);
648 assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649 assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651 // Clear the screen again, then append test text, then read it back and compare
652 let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656 Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659 term.clear_history();
660 term.clear();
661 let bg_save = term.text_bg_color();
662 let fg_save = term.text_fg_color();
663 term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664 term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665 term.append(test_text);
666 term.set_text_bg_color(bg_save);
667 term.set_text_fg_color(fg_save);
668
669 let mut text_out = read_disp(term);
670 // Trim trailing empty lines
671 text_out = text_out.trim_end_matches(&"\n").to_string();
672 assert_eq!(test_text, text_out);
673
674 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676 assert_eq!(term.ring_srow(), 0);
677 assert_eq!(term.ring_erow(), ring_rows - 1);
678 assert_eq!(
679 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680 term.display_rows()
681 );
682 assert_eq!(
683 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684 term.history_rows()
685 );
686
687 term.append(&format!(
688 "\n\nScreen has {} rows of {} columns.\n",
689 term.display_rows(),
690 term.display_columns()
691 ));
692
693 term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
Sourcepub fn hist_srow(&self) -> i32
pub fn hist_srow(&self) -> i32
Return the starting row# of the scrollback history.
Examples found in repository?
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555 let sel_len = term.selection_text_len();
556 let sel = term.selection_text();
557
558 term.take_focus().unwrap();
559 term.reset_terminal();
560 // Test the Utf8Char primitive
561 let uc = Utf8Char::new(b'Q');
562 let uc1 = uc.text_utf8();
563 assert_eq!(&uc1, b"Q");
564 assert_eq!(&uc.attrib(), &Attrib::Normal);
565 assert_eq!(
566 &uc.charflags(),
567 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568 );
569 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570 assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572 let ring_rows = term.ring_rows();
573
574 term.take_focus().unwrap();
575 term.clear_history();
576 assert_eq!(term.history_use(), 0);
577
578 // Subtract row numbers, modulo `rows`
579 fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580 match a - b {
581 n if n < 0 => n + rows,
582 n => n,
583 }
584 }
585 // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587 assert!(term.disp_srow() >= 0);
588 assert!(term.disp_erow() >= 0);
589 assert!(term.hist_srow() >= 0);
590 assert!(term.hist_erow() >= 0);
591 assert!(term.offset() >= 0);
592 assert!(term.disp_srow() <= ring_rows);
593 assert!(term.disp_erow() <= ring_rows);
594 assert!(term.hist_srow() <= ring_rows);
595 assert!(term.hist_erow() <= ring_rows);
596 assert!(term.offset() <= ring_rows);
597
598 assert_eq!(term.ring_srow(), 0);
599 assert_eq!(term.ring_erow(), ring_rows - 1);
600 assert_eq!(
601 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602 term.display_rows()
603 );
604 assert_eq!(
605 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606 term.history_rows()
607 );
608
609 assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610 assert_eq!(term.ring_srow(), 0);
611
612 /// Local function to read back all rows from the display into a long string.
613 /// Does not include scrollback history.
614 /// Trims trailing blanks on each line
615 fn read_disp(term: &Terminal) -> String {
616 let rows = term.display_rows();
617 let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618 for row in 0..rows {
619 let r = term.u8c_disp_row(row).trim();
620 // Iterate through a row, accumulating [u8]
621 for c in r.iter() {
622 // Note: Sometimes utf-8 length is > 1
623 text.extend_from_slice(c.text_utf8());
624 }
625 text.extend_from_slice(b"\n");
626 }
627 // Return the result as a string
628 std::str::from_utf8(&text).unwrap().to_string()
629 }
630
631 term.clear();
632 term.append("Top line ↑ (up-arrow)");
633 term.set_text_attrib(Attrib::Underline);
634 term.append(" ");
635 term.set_text_attrib(Attrib::Normal);
636 term.append(" \n");
637 let mut text_out = read_disp(term);
638 // Trim trailing empty lines
639 text_out = text_out.trim_end_matches(&"\n").to_string();
640 // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642 assert_eq!(text_out, "Top line ↑ (up-arrow) ");
643 let r = term.u8c_disp_row(0);
644 assert_eq!(r.col(0).text_utf8(), b"T");
645 assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646 assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647 let r = term.u8c_disp_row(1);
648 assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649 assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651 // Clear the screen again, then append test text, then read it back and compare
652 let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656 Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659 term.clear_history();
660 term.clear();
661 let bg_save = term.text_bg_color();
662 let fg_save = term.text_fg_color();
663 term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664 term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665 term.append(test_text);
666 term.set_text_bg_color(bg_save);
667 term.set_text_fg_color(fg_save);
668
669 let mut text_out = read_disp(term);
670 // Trim trailing empty lines
671 text_out = text_out.trim_end_matches(&"\n").to_string();
672 assert_eq!(test_text, text_out);
673
674 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676 assert_eq!(term.ring_srow(), 0);
677 assert_eq!(term.ring_erow(), ring_rows - 1);
678 assert_eq!(
679 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680 term.display_rows()
681 );
682 assert_eq!(
683 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684 term.history_rows()
685 );
686
687 term.append(&format!(
688 "\n\nScreen has {} rows of {} columns.\n",
689 term.display_rows(),
690 term.display_columns()
691 ));
692
693 term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
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?
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555 let sel_len = term.selection_text_len();
556 let sel = term.selection_text();
557
558 term.take_focus().unwrap();
559 term.reset_terminal();
560 // Test the Utf8Char primitive
561 let uc = Utf8Char::new(b'Q');
562 let uc1 = uc.text_utf8();
563 assert_eq!(&uc1, b"Q");
564 assert_eq!(&uc.attrib(), &Attrib::Normal);
565 assert_eq!(
566 &uc.charflags(),
567 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568 );
569 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570 assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572 let ring_rows = term.ring_rows();
573
574 term.take_focus().unwrap();
575 term.clear_history();
576 assert_eq!(term.history_use(), 0);
577
578 // Subtract row numbers, modulo `rows`
579 fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580 match a - b {
581 n if n < 0 => n + rows,
582 n => n,
583 }
584 }
585 // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587 assert!(term.disp_srow() >= 0);
588 assert!(term.disp_erow() >= 0);
589 assert!(term.hist_srow() >= 0);
590 assert!(term.hist_erow() >= 0);
591 assert!(term.offset() >= 0);
592 assert!(term.disp_srow() <= ring_rows);
593 assert!(term.disp_erow() <= ring_rows);
594 assert!(term.hist_srow() <= ring_rows);
595 assert!(term.hist_erow() <= ring_rows);
596 assert!(term.offset() <= ring_rows);
597
598 assert_eq!(term.ring_srow(), 0);
599 assert_eq!(term.ring_erow(), ring_rows - 1);
600 assert_eq!(
601 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602 term.display_rows()
603 );
604 assert_eq!(
605 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606 term.history_rows()
607 );
608
609 assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610 assert_eq!(term.ring_srow(), 0);
611
612 /// Local function to read back all rows from the display into a long string.
613 /// Does not include scrollback history.
614 /// Trims trailing blanks on each line
615 fn read_disp(term: &Terminal) -> String {
616 let rows = term.display_rows();
617 let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618 for row in 0..rows {
619 let r = term.u8c_disp_row(row).trim();
620 // Iterate through a row, accumulating [u8]
621 for c in r.iter() {
622 // Note: Sometimes utf-8 length is > 1
623 text.extend_from_slice(c.text_utf8());
624 }
625 text.extend_from_slice(b"\n");
626 }
627 // Return the result as a string
628 std::str::from_utf8(&text).unwrap().to_string()
629 }
630
631 term.clear();
632 term.append("Top line ↑ (up-arrow)");
633 term.set_text_attrib(Attrib::Underline);
634 term.append(" ");
635 term.set_text_attrib(Attrib::Normal);
636 term.append(" \n");
637 let mut text_out = read_disp(term);
638 // Trim trailing empty lines
639 text_out = text_out.trim_end_matches(&"\n").to_string();
640 // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642 assert_eq!(text_out, "Top line ↑ (up-arrow) ");
643 let r = term.u8c_disp_row(0);
644 assert_eq!(r.col(0).text_utf8(), b"T");
645 assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646 assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647 let r = term.u8c_disp_row(1);
648 assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649 assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651 // Clear the screen again, then append test text, then read it back and compare
652 let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656 Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659 term.clear_history();
660 term.clear();
661 let bg_save = term.text_bg_color();
662 let fg_save = term.text_fg_color();
663 term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664 term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665 term.append(test_text);
666 term.set_text_bg_color(bg_save);
667 term.set_text_fg_color(fg_save);
668
669 let mut text_out = read_disp(term);
670 // Trim trailing empty lines
671 text_out = text_out.trim_end_matches(&"\n").to_string();
672 assert_eq!(test_text, text_out);
673
674 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676 assert_eq!(term.ring_srow(), 0);
677 assert_eq!(term.ring_erow(), ring_rows - 1);
678 assert_eq!(
679 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680 term.display_rows()
681 );
682 assert_eq!(
683 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684 term.history_rows()
685 );
686
687 term.append(&format!(
688 "\n\nScreen has {} rows of {} columns.\n",
689 term.display_rows(),
690 term.display_columns()
691 ));
692
693 term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
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?
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555 let sel_len = term.selection_text_len();
556 let sel = term.selection_text();
557
558 term.take_focus().unwrap();
559 term.reset_terminal();
560 // Test the Utf8Char primitive
561 let uc = Utf8Char::new(b'Q');
562 let uc1 = uc.text_utf8();
563 assert_eq!(&uc1, b"Q");
564 assert_eq!(&uc.attrib(), &Attrib::Normal);
565 assert_eq!(
566 &uc.charflags(),
567 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568 );
569 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570 assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572 let ring_rows = term.ring_rows();
573
574 term.take_focus().unwrap();
575 term.clear_history();
576 assert_eq!(term.history_use(), 0);
577
578 // Subtract row numbers, modulo `rows`
579 fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580 match a - b {
581 n if n < 0 => n + rows,
582 n => n,
583 }
584 }
585 // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587 assert!(term.disp_srow() >= 0);
588 assert!(term.disp_erow() >= 0);
589 assert!(term.hist_srow() >= 0);
590 assert!(term.hist_erow() >= 0);
591 assert!(term.offset() >= 0);
592 assert!(term.disp_srow() <= ring_rows);
593 assert!(term.disp_erow() <= ring_rows);
594 assert!(term.hist_srow() <= ring_rows);
595 assert!(term.hist_erow() <= ring_rows);
596 assert!(term.offset() <= ring_rows);
597
598 assert_eq!(term.ring_srow(), 0);
599 assert_eq!(term.ring_erow(), ring_rows - 1);
600 assert_eq!(
601 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602 term.display_rows()
603 );
604 assert_eq!(
605 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606 term.history_rows()
607 );
608
609 assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610 assert_eq!(term.ring_srow(), 0);
611
612 /// Local function to read back all rows from the display into a long string.
613 /// Does not include scrollback history.
614 /// Trims trailing blanks on each line
615 fn read_disp(term: &Terminal) -> String {
616 let rows = term.display_rows();
617 let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618 for row in 0..rows {
619 let r = term.u8c_disp_row(row).trim();
620 // Iterate through a row, accumulating [u8]
621 for c in r.iter() {
622 // Note: Sometimes utf-8 length is > 1
623 text.extend_from_slice(c.text_utf8());
624 }
625 text.extend_from_slice(b"\n");
626 }
627 // Return the result as a string
628 std::str::from_utf8(&text).unwrap().to_string()
629 }
630
631 term.clear();
632 term.append("Top line ↑ (up-arrow)");
633 term.set_text_attrib(Attrib::Underline);
634 term.append(" ");
635 term.set_text_attrib(Attrib::Normal);
636 term.append(" \n");
637 let mut text_out = read_disp(term);
638 // Trim trailing empty lines
639 text_out = text_out.trim_end_matches(&"\n").to_string();
640 // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642 assert_eq!(text_out, "Top line ↑ (up-arrow) ");
643 let r = term.u8c_disp_row(0);
644 assert_eq!(r.col(0).text_utf8(), b"T");
645 assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646 assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647 let r = term.u8c_disp_row(1);
648 assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649 assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651 // Clear the screen again, then append test text, then read it back and compare
652 let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656 Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659 term.clear_history();
660 term.clear();
661 let bg_save = term.text_bg_color();
662 let fg_save = term.text_fg_color();
663 term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664 term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665 term.append(test_text);
666 term.set_text_bg_color(bg_save);
667 term.set_text_fg_color(fg_save);
668
669 let mut text_out = read_disp(term);
670 // Trim trailing empty lines
671 text_out = text_out.trim_end_matches(&"\n").to_string();
672 assert_eq!(test_text, text_out);
673
674 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676 assert_eq!(term.ring_srow(), 0);
677 assert_eq!(term.ring_erow(), ring_rows - 1);
678 assert_eq!(
679 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680 term.display_rows()
681 );
682 assert_eq!(
683 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684 term.history_rows()
685 );
686
687 term.append(&format!(
688 "\n\nScreen has {} rows of {} columns.\n",
689 term.display_rows(),
690 term.display_columns()
691 ));
692
693 term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
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?
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555 let sel_len = term.selection_text_len();
556 let sel = term.selection_text();
557
558 term.take_focus().unwrap();
559 term.reset_terminal();
560 // Test the Utf8Char primitive
561 let uc = Utf8Char::new(b'Q');
562 let uc1 = uc.text_utf8();
563 assert_eq!(&uc1, b"Q");
564 assert_eq!(&uc.attrib(), &Attrib::Normal);
565 assert_eq!(
566 &uc.charflags(),
567 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568 );
569 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570 assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572 let ring_rows = term.ring_rows();
573
574 term.take_focus().unwrap();
575 term.clear_history();
576 assert_eq!(term.history_use(), 0);
577
578 // Subtract row numbers, modulo `rows`
579 fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580 match a - b {
581 n if n < 0 => n + rows,
582 n => n,
583 }
584 }
585 // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587 assert!(term.disp_srow() >= 0);
588 assert!(term.disp_erow() >= 0);
589 assert!(term.hist_srow() >= 0);
590 assert!(term.hist_erow() >= 0);
591 assert!(term.offset() >= 0);
592 assert!(term.disp_srow() <= ring_rows);
593 assert!(term.disp_erow() <= ring_rows);
594 assert!(term.hist_srow() <= ring_rows);
595 assert!(term.hist_erow() <= ring_rows);
596 assert!(term.offset() <= ring_rows);
597
598 assert_eq!(term.ring_srow(), 0);
599 assert_eq!(term.ring_erow(), ring_rows - 1);
600 assert_eq!(
601 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602 term.display_rows()
603 );
604 assert_eq!(
605 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606 term.history_rows()
607 );
608
609 assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610 assert_eq!(term.ring_srow(), 0);
611
612 /// Local function to read back all rows from the display into a long string.
613 /// Does not include scrollback history.
614 /// Trims trailing blanks on each line
615 fn read_disp(term: &Terminal) -> String {
616 let rows = term.display_rows();
617 let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618 for row in 0..rows {
619 let r = term.u8c_disp_row(row).trim();
620 // Iterate through a row, accumulating [u8]
621 for c in r.iter() {
622 // Note: Sometimes utf-8 length is > 1
623 text.extend_from_slice(c.text_utf8());
624 }
625 text.extend_from_slice(b"\n");
626 }
627 // Return the result as a string
628 std::str::from_utf8(&text).unwrap().to_string()
629 }
630
631 term.clear();
632 term.append("Top line ↑ (up-arrow)");
633 term.set_text_attrib(Attrib::Underline);
634 term.append(" ");
635 term.set_text_attrib(Attrib::Normal);
636 term.append(" \n");
637 let mut text_out = read_disp(term);
638 // Trim trailing empty lines
639 text_out = text_out.trim_end_matches(&"\n").to_string();
640 // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642 assert_eq!(text_out, "Top line ↑ (up-arrow) ");
643 let r = term.u8c_disp_row(0);
644 assert_eq!(r.col(0).text_utf8(), b"T");
645 assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646 assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647 let r = term.u8c_disp_row(1);
648 assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649 assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651 // Clear the screen again, then append test text, then read it back and compare
652 let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656 Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659 term.clear_history();
660 term.clear();
661 let bg_save = term.text_bg_color();
662 let fg_save = term.text_fg_color();
663 term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664 term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665 term.append(test_text);
666 term.set_text_bg_color(bg_save);
667 term.set_text_fg_color(fg_save);
668
669 let mut text_out = read_disp(term);
670 // Trim trailing empty lines
671 text_out = text_out.trim_end_matches(&"\n").to_string();
672 assert_eq!(test_text, text_out);
673
674 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676 assert_eq!(term.ring_srow(), 0);
677 assert_eq!(term.ring_erow(), ring_rows - 1);
678 assert_eq!(
679 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680 term.display_rows()
681 );
682 assert_eq!(
683 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684 term.history_rows()
685 );
686
687 term.append(&format!(
688 "\n\nScreen has {} rows of {} columns.\n",
689 term.display_rows(),
690 term.display_columns()
691 ));
692
693 term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
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?
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555 let sel_len = term.selection_text_len();
556 let sel = term.selection_text();
557
558 term.take_focus().unwrap();
559 term.reset_terminal();
560 // Test the Utf8Char primitive
561 let uc = Utf8Char::new(b'Q');
562 let uc1 = uc.text_utf8();
563 assert_eq!(&uc1, b"Q");
564 assert_eq!(&uc.attrib(), &Attrib::Normal);
565 assert_eq!(
566 &uc.charflags(),
567 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568 );
569 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570 assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572 let ring_rows = term.ring_rows();
573
574 term.take_focus().unwrap();
575 term.clear_history();
576 assert_eq!(term.history_use(), 0);
577
578 // Subtract row numbers, modulo `rows`
579 fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580 match a - b {
581 n if n < 0 => n + rows,
582 n => n,
583 }
584 }
585 // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587 assert!(term.disp_srow() >= 0);
588 assert!(term.disp_erow() >= 0);
589 assert!(term.hist_srow() >= 0);
590 assert!(term.hist_erow() >= 0);
591 assert!(term.offset() >= 0);
592 assert!(term.disp_srow() <= ring_rows);
593 assert!(term.disp_erow() <= ring_rows);
594 assert!(term.hist_srow() <= ring_rows);
595 assert!(term.hist_erow() <= ring_rows);
596 assert!(term.offset() <= ring_rows);
597
598 assert_eq!(term.ring_srow(), 0);
599 assert_eq!(term.ring_erow(), ring_rows - 1);
600 assert_eq!(
601 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602 term.display_rows()
603 );
604 assert_eq!(
605 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606 term.history_rows()
607 );
608
609 assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610 assert_eq!(term.ring_srow(), 0);
611
612 /// Local function to read back all rows from the display into a long string.
613 /// Does not include scrollback history.
614 /// Trims trailing blanks on each line
615 fn read_disp(term: &Terminal) -> String {
616 let rows = term.display_rows();
617 let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618 for row in 0..rows {
619 let r = term.u8c_disp_row(row).trim();
620 // Iterate through a row, accumulating [u8]
621 for c in r.iter() {
622 // Note: Sometimes utf-8 length is > 1
623 text.extend_from_slice(c.text_utf8());
624 }
625 text.extend_from_slice(b"\n");
626 }
627 // Return the result as a string
628 std::str::from_utf8(&text).unwrap().to_string()
629 }
630
631 term.clear();
632 term.append("Top line ↑ (up-arrow)");
633 term.set_text_attrib(Attrib::Underline);
634 term.append(" ");
635 term.set_text_attrib(Attrib::Normal);
636 term.append(" \n");
637 let mut text_out = read_disp(term);
638 // Trim trailing empty lines
639 text_out = text_out.trim_end_matches(&"\n").to_string();
640 // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642 assert_eq!(text_out, "Top line ↑ (up-arrow) ");
643 let r = term.u8c_disp_row(0);
644 assert_eq!(r.col(0).text_utf8(), b"T");
645 assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646 assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647 let r = term.u8c_disp_row(1);
648 assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649 assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651 // Clear the screen again, then append test text, then read it back and compare
652 let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656 Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659 term.clear_history();
660 term.clear();
661 let bg_save = term.text_bg_color();
662 let fg_save = term.text_fg_color();
663 term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664 term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665 term.append(test_text);
666 term.set_text_bg_color(bg_save);
667 term.set_text_fg_color(fg_save);
668
669 let mut text_out = read_disp(term);
670 // Trim trailing empty lines
671 text_out = text_out.trim_end_matches(&"\n").to_string();
672 assert_eq!(test_text, text_out);
673
674 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676 assert_eq!(term.ring_srow(), 0);
677 assert_eq!(term.ring_erow(), ring_rows - 1);
678 assert_eq!(
679 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680 term.display_rows()
681 );
682 assert_eq!(
683 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684 term.history_rows()
685 );
686
687 term.append(&format!(
688 "\n\nScreen has {} rows of {} columns.\n",
689 term.display_rows(),
690 term.display_columns()
691 ));
692
693 term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
Sourcepub fn u8c_cursor(&self) -> Utf8Char
pub fn u8c_cursor(&self) -> Utf8Char
Return the Utf8Char
for character under cursor.
Examples found in repository?
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700 term.take_focus().unwrap();
701
702 // Test the attr_fg_color and attr_bg_color methods.
703 // Put a single character 'A' into the buffer and check it
704 term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705 term.set_text_bg_color(Color::TransparentBg);
706 term.set_text_fg_color(Color::XtermWhite);
707 term.append("A");
708 let r = &term.u8c_disp_row(0);
709 let uc = r.col(0);
710 assert_eq!(uc.text_utf8(), b"A");
711 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712 assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713 assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714 assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715 assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717 // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718 term.clear();
719 term.set_text_fg_color_xterm(XtermColor::White);
720 term.set_text_bg_color_xterm(XtermColor::Black);
721 assert_eq!(term.text_attrib(), Attrib::Normal);
722
723 assert!(term.ansi());
724 term.append("B\x1b[32mC\x1b[1mD\n");
725
726 let r = &term.u8c_disp_row(0);
727 let uc = r.col(0);
728 assert_eq!(uc.text_utf8(), b"B");
729 assert!(uc.is_char(b'B'));
730 assert!(!uc.is_char(b'A'));
731 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735 assert_eq!(
736 &uc.charflags(),
737 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738 );
739
740 let uc = r.col(1);
741 assert_eq!(uc.text_utf8(), b"C");
742 assert!(uc.is_char(b'C'));
743 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747 assert_eq!(
748 &uc.charflags(),
749 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750 );
751
752 let uc = r.col(2);
753 assert_eq!(uc.text_utf8(), b"D");
754 assert!(uc.is_char(b'D'));
755 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757 assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759 assert_eq!(
760 &uc.attr_fgcolor(Some(term)),
761 &Color::from_rgb(0x20, 0xf0, 0x20)
762 );
763 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764 assert_eq!(
765 &uc.charflags(),
766 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767 );
768
769 // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770 term.clear();
771 term.set_text_fg_color_xterm(XtermColor::White);
772 term.set_text_bg_color_xterm(XtermColor::Black);
773 term.set_text_attrib(Attrib::Normal);
774 assert_eq!(term.text_attrib(), Attrib::Normal);
775
776 assert!(term.ansi());
777 term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779 let r = &term.u8c_disp_row(0);
780 let uc = r.col(0);
781 assert_eq!(uc.text_utf8(), b"B");
782 assert!(uc.is_char(b'B'));
783 assert!(!uc.is_char(b'A'));
784 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788 assert_eq!(
789 &uc.charflags(),
790 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791 );
792
793 let uc = r.col(1);
794 assert_eq!(uc.text_utf8(), b"C");
795 assert!(uc.is_char(b'C'));
796 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800 assert_eq!(
801 &uc.charflags(),
802 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803 );
804
805 let uc = r.col(2);
806 assert_eq!(uc.text_utf8(), b"D");
807 assert!(uc.is_char(b'D'));
808 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812 assert_eq!(
813 &uc.charflags(),
814 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815 );
816
817 let uc = r.col(3);
818 assert_eq!(uc.text_utf8(), b"E");
819 assert!(uc.is_char(b'E'));
820 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822 assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823 assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824 assert_eq!(
825 &uc.charflags(),
826 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827 );
828
829 // Test some miscellaneous Utf8 constants
830 assert_eq!(uc.length(), 1);
831 assert_eq!(uc.max_utf8(), 4);
832 assert_eq!(uc.pwidth(), 9.0);
833 assert_eq!(uc.pwidth_int(), 9);
834
835 term.set_text_fg_color_xterm(XtermColor::White);
836 term.set_text_bg_color_xterm(XtermColor::Black);
837 term.clear();
838 term.set_text_attrib(Attrib::Normal);
839
840 // Mouse selection functions
841 term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842 term.clear_mouse_selection();
843 assert_eq!(term.get_selection(), None);
844
845 // Play with cursor position
846 term.append("0123456789\n"); // Set up test pattern
847 term.append("ABCDEFGHIJ\n");
848 term.append("abcdefghij\n");
849
850 term.set_cursor_row(1);
851 assert_eq!(term.cursor_row(), 1);
852 term.set_cursor_col(1);
853 assert_eq!(term.cursor_col(), 1);
854 assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856 term.append("----"); // Overwrites text at cursor and moves cursor forward
857 assert_eq!(term.cursor_row(), 1);
858 assert_eq!(term.cursor_col(), 5);
859 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860 term.set_cursor_col(1);
861 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863 term.cursor_up(1, false);
864 assert_eq!(term.cursor_row(), 0);
865 assert_eq!(term.cursor_col(), 1);
866 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868 // Hit top of screen, so nothing happens
869 term.cursor_up(1, false);
870 assert_eq!(term.cursor_row(), 0);
871 assert_eq!(term.cursor_col(), 1);
872 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874 // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875 term.cursor_up(1, true);
876 assert_eq!(term.cursor_row(), 0);
877 assert_eq!(term.cursor_col(), 1);
878 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880 // Go back down to the overwritten text
881 term.cursor_down(2, false);
882 assert_eq!(term.cursor_row(), 2);
883 assert_eq!(term.cursor_col(), 1);
884 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886 // Go right past the overwritten text
887 term.cursor_right(4, false);
888 assert_eq!(term.cursor_row(), 2);
889 assert_eq!(term.cursor_col(), 5);
890 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892 // Go left to the end of the overwritten text
893 term.cursor_left(1);
894 assert_eq!(term.cursor_row(), 2);
895 assert_eq!(term.cursor_col(), 4);
896 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898 // Scroll back down, removing the blank line at the top.
899 // Cursor stays in place, the text moves under it.
900 term.scroll(1);
901 assert_eq!(term.cursor_row(), 2);
902 assert_eq!(term.cursor_col(), 4);
903 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905 // Clear from here to end-of-line
906 term.clear_eol();
907 assert_eq!(term.cursor_row(), 2);
908 assert_eq!(term.cursor_col(), 4);
909 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911 // Now clear from here to start-of-line. Cursor does not move.
912 term.clear_sol();
913 assert_eq!(term.cursor_row(), 2);
914 assert_eq!(term.cursor_col(), 4);
915 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916 term.cursor_left(1);
917 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918 term.set_cursor_col(0);
919 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921 // Clear some lines
922 term.clear_line(1);
923 assert_eq!(term.cursor_row(), 2);
924 assert_eq!(term.cursor_col(), 0);
925 term.set_cursor_row(1);
926 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927 term.set_cursor_row(3);
928 term.clear_cur_line();
929 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930 assert_eq!(term.cursor_row(), 3);
931 assert_eq!(term.cursor_col(), 0);
932
933 term.append("Two lines above are intentionally left blank.\n");
934 assert_eq!(term.cursor_row(), 4);
935 assert_eq!(term.cursor_col(), 0);
936
937 // Set up the test pattern again, then play with insert/delete
938 term.append("0123456789\n");
939 term.append("ABCDEFGHIJ\n");
940 term.append("abcdefghij\n");
941 assert_eq!(term.cursor_row(), 7);
942
943 term.set_cursor_row(4);
944 term.set_cursor_col(4);
945 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947 term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949 term.cursor_right(5, false);
950 assert_eq!(term.cursor_col(), 9);
951 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953 // Insert two blank rows above cursor. Cursor stays put.
954 term.insert_rows(2);
955 assert_eq!(term.cursor_row(), 4);
956 assert_eq!(term.cursor_col(), 9);
957 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958 term.cursor_down(2, false); // Go down to find our text again
959 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961 // Go back to the beginning of the inserted 'x' characters and delete them.
962 term.cursor_left(5);
963 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964 term.delete_cur_chars(5);
965 assert_eq!(term.cursor_row(), 6);
966 assert_eq!(term.cursor_col(), 4);
967 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969 term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970 term.cursor_down(1, false);
971 term.cursor_left(2);
972 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974 term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975 assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976 term.cursor_up(1, false);
977 term.delete_rows(2); // Delete remains of test pattern
978
979 term.set_text_attrib(Attrib::Bold);
980 term.insert_char_eol('-', 3, 15, 20);
981 term.set_cursor_row(3);
982 term.set_cursor_col(15);
983 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984 assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986 term.set_text_attrib(Attrib::Italic);
987 term.append(" and all lines below");
988 term.set_text_attrib(Attrib::Normal);
989 term.cursor_down(1, false);
990
991 let mut hsb = term.hscrollbar();
992 let mut sb = term.scrollbar();
993 hsb.set_value(100.0);
994 assert_eq!(hsb.value(), 100.0);
995 sb.set_value(50.0);
996 assert_eq!(sb.value(), 50.0);
997 hsb.set_value(0.0);
998 assert_eq!(hsb.value(), 0.0);
999 sb.set_value(0.0);
1000 assert_eq!(sb.value(), 0.0);
1001}
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?
554fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
555 let sel_len = term.selection_text_len();
556 let sel = term.selection_text();
557
558 term.take_focus().unwrap();
559 term.reset_terminal();
560 // Test the Utf8Char primitive
561 let uc = Utf8Char::new(b'Q');
562 let uc1 = uc.text_utf8();
563 assert_eq!(&uc1, b"Q");
564 assert_eq!(&uc.attrib(), &Attrib::Normal);
565 assert_eq!(
566 &uc.charflags(),
567 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
568 );
569 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
570 assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
571
572 let ring_rows = term.ring_rows();
573
574 term.take_focus().unwrap();
575 term.clear_history();
576 assert_eq!(term.history_use(), 0);
577
578 // Subtract row numbers, modulo `rows`
579 fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
580 match a - b {
581 n if n < 0 => n + rows,
582 n => n,
583 }
584 }
585 // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
586 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
587 assert!(term.disp_srow() >= 0);
588 assert!(term.disp_erow() >= 0);
589 assert!(term.hist_srow() >= 0);
590 assert!(term.hist_erow() >= 0);
591 assert!(term.offset() >= 0);
592 assert!(term.disp_srow() <= ring_rows);
593 assert!(term.disp_erow() <= ring_rows);
594 assert!(term.hist_srow() <= ring_rows);
595 assert!(term.hist_erow() <= ring_rows);
596 assert!(term.offset() <= ring_rows);
597
598 assert_eq!(term.ring_srow(), 0);
599 assert_eq!(term.ring_erow(), ring_rows - 1);
600 assert_eq!(
601 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
602 term.display_rows()
603 );
604 assert_eq!(
605 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
606 term.history_rows()
607 );
608
609 assert_eq!(term.ring_erow(), term.ring_rows() - 1);
610 assert_eq!(term.ring_srow(), 0);
611
612 /// Local function to read back all rows from the display into a long string.
613 /// Does not include scrollback history.
614 /// Trims trailing blanks on each line
615 fn read_disp(term: &Terminal) -> String {
616 let rows = term.display_rows();
617 let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
618 for row in 0..rows {
619 let r = term.u8c_disp_row(row).trim();
620 // Iterate through a row, accumulating [u8]
621 for c in r.iter() {
622 // Note: Sometimes utf-8 length is > 1
623 text.extend_from_slice(c.text_utf8());
624 }
625 text.extend_from_slice(b"\n");
626 }
627 // Return the result as a string
628 std::str::from_utf8(&text).unwrap().to_string()
629 }
630
631 term.clear();
632 term.append("Top line ↑ (up-arrow)");
633 term.set_text_attrib(Attrib::Underline);
634 term.append(" ");
635 term.set_text_attrib(Attrib::Normal);
636 term.append(" \n");
637 let mut text_out = read_disp(term);
638 // Trim trailing empty lines
639 text_out = text_out.trim_end_matches(&"\n").to_string();
640 // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
641
642 assert_eq!(text_out, "Top line ↑ (up-arrow) ");
643 let r = term.u8c_disp_row(0);
644 assert_eq!(r.col(0).text_utf8(), b"T");
645 assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
646 assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
647 let r = term.u8c_disp_row(1);
648 assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
649 assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
650
651 // Clear the screen again, then append test text, then read it back and compare
652 let test_text = "The wind was a torrent of darkness among the gusty trees.
653The moon was a ghostly galleon tossed upon cloudy seas.
654The road was a ribbon of moonlight over the purple moor,
655And the highwayman came riding—
656 Riding—riding—
657The highwayman came riding, up to the old inn-door.";
658
659 term.clear_history();
660 term.clear();
661 let bg_save = term.text_bg_color();
662 let fg_save = term.text_fg_color();
663 term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
664 term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
665 term.append(test_text);
666 term.set_text_bg_color(bg_save);
667 term.set_text_fg_color(fg_save);
668
669 let mut text_out = read_disp(term);
670 // Trim trailing empty lines
671 text_out = text_out.trim_end_matches(&"\n").to_string();
672 assert_eq!(test_text, text_out);
673
674 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
675
676 assert_eq!(term.ring_srow(), 0);
677 assert_eq!(term.ring_erow(), ring_rows - 1);
678 assert_eq!(
679 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
680 term.display_rows()
681 );
682 assert_eq!(
683 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
684 term.history_rows()
685 );
686
687 term.append(&format!(
688 "\n\nScreen has {} rows of {} columns.\n",
689 term.display_rows(),
690 term.display_columns()
691 ));
692
693 term.append(&format!("Selection len: {sel_len}\nSelection: '{sel:?}'\n"));
694}
695
696//--------------------------------------------------------------------------------------
697/// Yet another set of tests for misc cursor functions and other stuff
698/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
699fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
700 term.take_focus().unwrap();
701
702 // Test the attr_fg_color and attr_bg_color methods.
703 // Put a single character 'A' into the buffer and check it
704 term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
705 term.set_text_bg_color(Color::TransparentBg);
706 term.set_text_fg_color(Color::XtermWhite);
707 term.append("A");
708 let r = &term.u8c_disp_row(0);
709 let uc = r.col(0);
710 assert_eq!(uc.text_utf8(), b"A");
711 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
712 assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
713 assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
714 assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
715 assert_eq!(&uc.attrib(), &Attrib::Normal);
716
717 // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
718 term.clear();
719 term.set_text_fg_color_xterm(XtermColor::White);
720 term.set_text_bg_color_xterm(XtermColor::Black);
721 assert_eq!(term.text_attrib(), Attrib::Normal);
722
723 assert!(term.ansi());
724 term.append("B\x1b[32mC\x1b[1mD\n");
725
726 let r = &term.u8c_disp_row(0);
727 let uc = r.col(0);
728 assert_eq!(uc.text_utf8(), b"B");
729 assert!(uc.is_char(b'B'));
730 assert!(!uc.is_char(b'A'));
731 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
732 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
733 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
734 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
735 assert_eq!(
736 &uc.charflags(),
737 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
738 );
739
740 let uc = r.col(1);
741 assert_eq!(uc.text_utf8(), b"C");
742 assert!(uc.is_char(b'C'));
743 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
744 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
745 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
746 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
747 assert_eq!(
748 &uc.charflags(),
749 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
750 );
751
752 let uc = r.col(2);
753 assert_eq!(uc.text_utf8(), b"D");
754 assert!(uc.is_char(b'D'));
755 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
756 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
757 assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
758 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
759 assert_eq!(
760 &uc.attr_fgcolor(Some(term)),
761 &Color::from_rgb(0x20, 0xf0, 0x20)
762 );
763 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
764 assert_eq!(
765 &uc.charflags(),
766 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
767 );
768
769 // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
770 term.clear();
771 term.set_text_fg_color_xterm(XtermColor::White);
772 term.set_text_bg_color_xterm(XtermColor::Black);
773 term.set_text_attrib(Attrib::Normal);
774 assert_eq!(term.text_attrib(), Attrib::Normal);
775
776 assert!(term.ansi());
777 term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
778
779 let r = &term.u8c_disp_row(0);
780 let uc = r.col(0);
781 assert_eq!(uc.text_utf8(), b"B");
782 assert!(uc.is_char(b'B'));
783 assert!(!uc.is_char(b'A'));
784 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
785 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
786 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
787 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
788 assert_eq!(
789 &uc.charflags(),
790 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
791 );
792
793 let uc = r.col(1);
794 assert_eq!(uc.text_utf8(), b"C");
795 assert!(uc.is_char(b'C'));
796 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
797 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
798 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
799 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
800 assert_eq!(
801 &uc.charflags(),
802 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
803 );
804
805 let uc = r.col(2);
806 assert_eq!(uc.text_utf8(), b"D");
807 assert!(uc.is_char(b'D'));
808 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
809 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
810 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
811 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
812 assert_eq!(
813 &uc.charflags(),
814 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
815 );
816
817 let uc = r.col(3);
818 assert_eq!(uc.text_utf8(), b"E");
819 assert!(uc.is_char(b'E'));
820 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
821 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
822 assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
823 assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
824 assert_eq!(
825 &uc.charflags(),
826 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
827 );
828
829 // Test some miscellaneous Utf8 constants
830 assert_eq!(uc.length(), 1);
831 assert_eq!(uc.max_utf8(), 4);
832 assert_eq!(uc.pwidth(), 9.0);
833 assert_eq!(uc.pwidth_int(), 9);
834
835 term.set_text_fg_color_xterm(XtermColor::White);
836 term.set_text_bg_color_xterm(XtermColor::Black);
837 term.clear();
838 term.set_text_attrib(Attrib::Normal);
839
840 // Mouse selection functions
841 term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
842 term.clear_mouse_selection();
843 assert_eq!(term.get_selection(), None);
844
845 // Play with cursor position
846 term.append("0123456789\n"); // Set up test pattern
847 term.append("ABCDEFGHIJ\n");
848 term.append("abcdefghij\n");
849
850 term.set_cursor_row(1);
851 assert_eq!(term.cursor_row(), 1);
852 term.set_cursor_col(1);
853 assert_eq!(term.cursor_col(), 1);
854 assert_eq!(term.u8c_cursor().text_utf8(), b"1");
855
856 term.append("----"); // Overwrites text at cursor and moves cursor forward
857 assert_eq!(term.cursor_row(), 1);
858 assert_eq!(term.cursor_col(), 5);
859 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
860 term.set_cursor_col(1);
861 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
862
863 term.cursor_up(1, false);
864 assert_eq!(term.cursor_row(), 0);
865 assert_eq!(term.cursor_col(), 1);
866 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
867
868 // Hit top of screen, so nothing happens
869 term.cursor_up(1, false);
870 assert_eq!(term.cursor_row(), 0);
871 assert_eq!(term.cursor_col(), 1);
872 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
873
874 // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
875 term.cursor_up(1, true);
876 assert_eq!(term.cursor_row(), 0);
877 assert_eq!(term.cursor_col(), 1);
878 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
879
880 // Go back down to the overwritten text
881 term.cursor_down(2, false);
882 assert_eq!(term.cursor_row(), 2);
883 assert_eq!(term.cursor_col(), 1);
884 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
885
886 // Go right past the overwritten text
887 term.cursor_right(4, false);
888 assert_eq!(term.cursor_row(), 2);
889 assert_eq!(term.cursor_col(), 5);
890 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
891
892 // Go left to the end of the overwritten text
893 term.cursor_left(1);
894 assert_eq!(term.cursor_row(), 2);
895 assert_eq!(term.cursor_col(), 4);
896 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
897
898 // Scroll back down, removing the blank line at the top.
899 // Cursor stays in place, the text moves under it.
900 term.scroll(1);
901 assert_eq!(term.cursor_row(), 2);
902 assert_eq!(term.cursor_col(), 4);
903 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
904
905 // Clear from here to end-of-line
906 term.clear_eol();
907 assert_eq!(term.cursor_row(), 2);
908 assert_eq!(term.cursor_col(), 4);
909 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
910
911 // Now clear from here to start-of-line. Cursor does not move.
912 term.clear_sol();
913 assert_eq!(term.cursor_row(), 2);
914 assert_eq!(term.cursor_col(), 4);
915 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
916 term.cursor_left(1);
917 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
918 term.set_cursor_col(0);
919 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
920
921 // Clear some lines
922 term.clear_line(1);
923 assert_eq!(term.cursor_row(), 2);
924 assert_eq!(term.cursor_col(), 0);
925 term.set_cursor_row(1);
926 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
927 term.set_cursor_row(3);
928 term.clear_cur_line();
929 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930 assert_eq!(term.cursor_row(), 3);
931 assert_eq!(term.cursor_col(), 0);
932
933 term.append("Two lines above are intentionally left blank.\n");
934 assert_eq!(term.cursor_row(), 4);
935 assert_eq!(term.cursor_col(), 0);
936
937 // Set up the test pattern again, then play with insert/delete
938 term.append("0123456789\n");
939 term.append("ABCDEFGHIJ\n");
940 term.append("abcdefghij\n");
941 assert_eq!(term.cursor_row(), 7);
942
943 term.set_cursor_row(4);
944 term.set_cursor_col(4);
945 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
946
947 term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
948 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
949 term.cursor_right(5, false);
950 assert_eq!(term.cursor_col(), 9);
951 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
952
953 // Insert two blank rows above cursor. Cursor stays put.
954 term.insert_rows(2);
955 assert_eq!(term.cursor_row(), 4);
956 assert_eq!(term.cursor_col(), 9);
957 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
958 term.cursor_down(2, false); // Go down to find our text again
959 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
960
961 // Go back to the beginning of the inserted 'x' characters and delete them.
962 term.cursor_left(5);
963 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
964 term.delete_cur_chars(5);
965 assert_eq!(term.cursor_row(), 6);
966 assert_eq!(term.cursor_col(), 4);
967 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
968
969 term.delete_chars(7, 2, 2); // Delete "CD" from the next row
970 term.cursor_down(1, false);
971 term.cursor_left(2);
972 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
973
974 term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
975 assert_eq!(term.u8c_cursor().text_utf8(), b"c");
976 term.cursor_up(1, false);
977 term.delete_rows(2); // Delete remains of test pattern
978
979 term.set_text_attrib(Attrib::Bold);
980 term.insert_char_eol('-', 3, 15, 20);
981 term.set_cursor_row(3);
982 term.set_cursor_col(15);
983 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
984 assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
985
986 term.set_text_attrib(Attrib::Italic);
987 term.append(" and all lines below");
988 term.set_text_attrib(Attrib::Normal);
989 term.cursor_down(1, false);
990
991 let mut hsb = term.hscrollbar();
992 let mut sb = term.scrollbar();
993 hsb.set_value(100.0);
994 assert_eq!(hsb.value(), 100.0);
995 sb.set_value(50.0);
996 assert_eq!(sb.value(), 50.0);
997 hsb.set_value(0.0);
998 assert_eq!(hsb.value(), 0.0);
999 sb.set_value(0.0);
1000 assert_eq!(sb.value(), 0.0);
1001}
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§impl IntoIterator for Terminal
impl IntoIterator for Terminal
Source§impl WidgetBase for Terminal
impl WidgetBase for Terminal
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 set_label(&mut self, title: &str)
fn set_label(&mut self, title: &str)
@
sign.
and for the associated formatting.Source§fn unset_label(&mut self)
fn unset_label(&mut self)
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_when(&mut self, trigger: When)
fn set_when(&mut self, trigger: When)
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§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§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 as_widget<W: WidgetBase>(&self) -> W
unsafe fn as_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 WidgetSource§impl WidgetProps for Terminal
impl WidgetProps for Terminal
Source§fn with_label(self, title: &str) -> Self
fn with_label(self, title: &str) -> Self
Initialize with a label
Source§fn with_align(self, align: Align) -> Self
fn with_align(self, align: Align) -> Self
Initialize with alignment
Source§fn with_type<T: WidgetType>(self, typ: T) -> Self
fn with_type<T: WidgetType>(self, typ: T) -> Self
Initialize with type
Source§fn below_of<W: WidgetExt>(self, wid: &W, padding: i32) -> Self
fn below_of<W: WidgetExt>(self, wid: &W, padding: i32) -> Self
Initialize at bottom of another widget
Source§fn above_of<W: WidgetExt>(self, wid: &W, padding: i32) -> Self
fn above_of<W: WidgetExt>(self, wid: &W, padding: i32) -> Self
Initialize above of another widget
Source§fn right_of<W: WidgetExt>(self, wid: &W, padding: i32) -> Self
fn right_of<W: WidgetExt>(self, wid: &W, padding: i32) -> Self
Initialize right of another widget
Source§fn left_of<W: WidgetExt>(self, wid: &W, padding: i32) -> Self
fn left_of<W: WidgetExt>(self, wid: &W, padding: i32) -> Self
Initialize left of another widget
Source§fn center_x<W: WidgetExt>(self, w: &W) -> Self
fn center_x<W: WidgetExt>(self, w: &W) -> Self
Initialize center of another widget on the x axis
Source§fn center_y<W: WidgetExt>(self, w: &W) -> Self
fn center_y<W: WidgetExt>(self, w: &W) -> Self
Initialize center of another widget on the y axis
Source§fn center_of_parent(self) -> Self
fn center_of_parent(self) -> Self
Initialize center of parent
Source§fn size_of_parent(self) -> Self
fn size_of_parent(self) -> Self
Initialize to the size of the parent
impl Eq for Terminal
impl Send for Terminal
single-threaded
only.impl Sync for Terminal
single-threaded
only.