Struct fltk::group::experimental::Terminal
source · pub struct Terminal { /* private fields */ }
Expand description
Creates a scrollable display widget to handle terminal-like behavior, such as logging events or debug information. Replaces SimpleTerminal widget
Implementations§
source§impl Terminal
impl Terminal
sourcepub fn ansi(&self) -> bool
pub fn ansi(&self) -> bool
Returns whether the terminal is in ANSI mode.
Examples found in repository?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
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?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
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?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
//--------------------------------------------------------------------------------------
/// More tests that run when the menu bar Test1 is clicked
fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
term.reset_terminal();
term.append("0123456789 0\n");
term.append("0123456789 1\n");
term.append("0123456789 2\n");
term.append("0123456789 3\n");
term.append("0123456789 4\n");
term.append("0123456789 5\n");
term.append("0123456789 6\n");
term.append("0123456789 7\n");
term.append("0123456789 8\n");
term.append("0123456789 9\n");
term.append("------------\n");
term.set_text_fg_color(Color::Green);
term.put_char('A', 0, 0);
term.put_char('B', 1, 1);
term.put_char('C', 2, 2);
term.put_char('D', 3, 3);
term.put_char('E', 4, 4);
term.put_char('F', 5, 5);
term.set_text_fg_color(Color::XtermWhite);
assert_eq!(term.cursor_row(), 11);
assert_eq!(term.cursor_col(), 0);
term.set_text_bg_color(Color::DarkBlue);
term.put_char_utf8('b', 8, 1);
term.put_char_utf8('↑', 9, 1);
term.put_char_utf8('c', 8, 2);
term.put_char_utf8('↑', 9, 2);
term.put_char_utf8('d', 8, 3);
term.put_char_utf8('↑', 9, 3);
term.put_char_utf8('e', 8, 4);
term.put_char_utf8('↑', 9, 4);
term.put_char_utf8('f', 8, 5);
term.put_char_utf8('↑', 9, 5);
term.put_char_utf8('g', 8, 6);
term.put_char_utf8('↑', 9, 6);
term.set_text_bg_color(Color::Black);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("Done!\n");
term.set_text_attrib(Attrib::Normal);
}
//--------------------------------------------------------------------------------------
/// More tests that run when the menu bar button Test2 is clicked
fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
term.reset_terminal();
for i in 0..50 {
term.append(&format!("{i}\n"));
}
assert_eq!(term.cursor_row(), 30);
assert_eq!(term.display_rows(), 31);
assert_eq!(term.history_rows(), 100);
assert_eq!(term.history_lines(), 100);
assert_eq!(term.history_use(), 20);
term.clear_history();
assert_eq!(term.history_use(), 0);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nDone!\n");
term.set_text_attrib(Attrib::Normal);
}
//--------------------------------------------------------------------------------------
/// Another set of tests that run when Test3 is clicked
fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let hist = term.history_use();
assert_ne!(hist, 0);
term.clear();
assert_eq!(term.cursor_row(), 0);
assert_eq!(term.history_use(), hist + term.display_rows()); // A screenful of lines added to history
term.append("Test\ntext\na\nb\nc\nd");
assert_eq!(term.cursor_row(), 5);
let hist = term.history_use();
term.clear_screen_home(false);
assert_eq!(term.cursor_row(), 0);
assert_eq!(term.history_use(), hist); // History not changed
term.append("Test\ntext\na\nb\nc\nd\ne");
assert_eq!(term.cursor_row(), 6);
term.clear_screen_home(true);
assert_eq!(term.cursor_row(), 0);
let hist = term.history_use();
term.append("Test\ntext\na\nb\nc\n");
assert_eq!(term.cursor_row(), 5);
term.clear_to_color(Color::DarkBlue);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
assert!(term.history_use() > hist); // Some lines added to history
assert_eq!(term.cursor_row(), 0);
// Test cursor_home()
term.append("Test\n\n\n\n\n\n\n\n\n\n");
assert_eq!(term.cursor_row(), 10);
term.cursor_home();
assert_eq!(term.cursor_row(), 0);
// Test the widget color
assert_eq!(term.color(), Color::Black); // Default
term.set_color(Color::DarkGreen);
assert_eq!(term.color(), Color::DarkGreen);
term.set_color(Color::Black);
assert_eq!(term.color(), Color::Black);
term.append(
"This should be one line of white text on black, embedded into the top of a blue field.\n",
);
assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
term.set_output_translate(OutFlags::OFF);
assert_eq!(term.output_translate(), OutFlags::OFF);
term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nDone!\n");
term.set_text_attrib(Attrib::Normal);
}
//--------------------------------------------------------------------------------------
/// Another set of tests for the ring-buffer access methods
fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
// Test the Utf8Char primitive
let uc = Utf8Char::new(b'Q');
let uc1 = uc.text_utf8();
assert_eq!(&uc1, &[b'Q']);
assert_eq!(&uc.attrib(), &Attrib::Normal);
assert_eq!(
&uc.charflags(),
&(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
);
assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
let ring_rows = term.ring_rows();
// println!();
// dbg!(term.disp_srow(), term.disp_erow(), term.disp_rows(), term.ring_cols(), term.ring_srow(), term.ring_erow() );
// dbg!(term.hist_srow(), term.hist_erow(), term.hist_rows(), ring_rows );
// dbg!(term.offset(), term.hist_use_srow(), term.hist_use() );
term.take_focus().unwrap();
term.clear_history();
assert_eq!(term.history_use(), 0);
// Subtract row numbers, modulo `rows`
fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
match a - b {
n if n < 0 => n + rows,
n => n
}
}
// disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert!(term.disp_srow() >= 0);
assert!(term.disp_erow() >= 0);
assert!(term.hist_srow() >= 0);
assert!(term.hist_erow() >= 0);
assert!(term.offset() >= 0);
assert!(term.disp_srow() <= ring_rows);
assert!(term.disp_erow() <= ring_rows);
assert!(term.hist_srow() <= ring_rows);
assert!(term.hist_erow() <= ring_rows);
assert!(term.offset() <= ring_rows);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
assert_eq!(term.ring_erow(), term.ring_rows() - 1);
assert_eq!(term.ring_srow(), 0);
// Check the different cols methods, which should all return the same answer
assert!(term.disp_cols() > 10);
assert_eq!(term.disp_cols(), term.ring_cols());
assert_eq!(term.disp_cols(), term.hist_cols());
// Redundant protected vs public methods:
assert_eq!(term.disp_cols(), term.display_columns());
assert_eq!(term.disp_rows(), term.display_rows());
/// Local function to read back all rows from the display into a long string.
/// Does not include scrollback history.
/// Trims trailing blanks on each line
fn read_disp(term: &Terminal) -> String {
let rows = term.display_rows();
let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
for row in 0..rows {
let r = term.u8c_disp_row(row).trim();
// Iterate through a row, accumulating [u8]
for c in r.iter() {
// Note: Sometimes utf-8 length is > 1
text.extend_from_slice(c.text_utf8());
}
text.extend_from_slice(b"\n");
}
// Return the result as a string
std::str::from_utf8(&text).unwrap().to_string()
}
term.clear();
term.append("Top line ↑ (up-arrow)");
term.set_text_attrib(Attrib::Underline);
term.append(" ");
term.set_text_attrib(Attrib::Normal);
term.append(" \n");
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
// The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
assert_eq!(text_out, "Top line ↑ (up-arrow) \n");
let r = term.u8c_disp_row(0);
assert_eq!(r.col(0).text_utf8(), b"T");
assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
let r = term.u8c_disp_row(1);
assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
// Clear the screen again, then append test text, then read it back and compare
let test_text =
"The wind was a torrent of darkness among the gusty trees.
The moon was a ghostly galleon tossed upon cloudy seas.
The road was a ribbon of moonlight over the purple moor,
And the highwayman came riding—
Riding—riding—
The highwayman came riding, up to the old inn-door.";
term.clear_history();
term.clear();
let bg_save = term.text_bg_color();
let fg_save = term.text_fg_color();
term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
term.append(test_text);
term.set_text_bg_color(bg_save);
term.set_text_fg_color(fg_save);
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
assert_eq!(test_text, text_out);
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
term.append(&format!(
"\n\nScreen has {} rows of {} columns.\n",
term.disp_rows(),
term.disp_cols()
));
term.set_text_attrib(Attrib::Italic);
term.append("Done!");
term.set_text_attrib(Attrib::Normal);
}
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?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
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?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
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?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
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?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
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?
446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let hist = term.history_use();
assert_ne!(hist, 0);
term.clear();
assert_eq!(term.cursor_row(), 0);
assert_eq!(term.history_use(), hist + term.display_rows()); // A screenful of lines added to history
term.append("Test\ntext\na\nb\nc\nd");
assert_eq!(term.cursor_row(), 5);
let hist = term.history_use();
term.clear_screen_home(false);
assert_eq!(term.cursor_row(), 0);
assert_eq!(term.history_use(), hist); // History not changed
term.append("Test\ntext\na\nb\nc\nd\ne");
assert_eq!(term.cursor_row(), 6);
term.clear_screen_home(true);
assert_eq!(term.cursor_row(), 0);
let hist = term.history_use();
term.append("Test\ntext\na\nb\nc\n");
assert_eq!(term.cursor_row(), 5);
term.clear_to_color(Color::DarkBlue);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
assert!(term.history_use() > hist); // Some lines added to history
assert_eq!(term.cursor_row(), 0);
// Test cursor_home()
term.append("Test\n\n\n\n\n\n\n\n\n\n");
assert_eq!(term.cursor_row(), 10);
term.cursor_home();
assert_eq!(term.cursor_row(), 0);
// Test the widget color
assert_eq!(term.color(), Color::Black); // Default
term.set_color(Color::DarkGreen);
assert_eq!(term.color(), Color::DarkGreen);
term.set_color(Color::Black);
assert_eq!(term.color(), Color::Black);
term.append(
"This should be one line of white text on black, embedded into the top of a blue field.\n",
);
assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
term.set_output_translate(OutFlags::OFF);
assert_eq!(term.output_translate(), OutFlags::OFF);
term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nDone!\n");
term.set_text_attrib(Attrib::Normal);
}
//--------------------------------------------------------------------------------------
/// Another set of tests for the ring-buffer access methods
fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
// Test the Utf8Char primitive
let uc = Utf8Char::new(b'Q');
let uc1 = uc.text_utf8();
assert_eq!(&uc1, &[b'Q']);
assert_eq!(&uc.attrib(), &Attrib::Normal);
assert_eq!(
&uc.charflags(),
&(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
);
assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
let ring_rows = term.ring_rows();
// println!();
// dbg!(term.disp_srow(), term.disp_erow(), term.disp_rows(), term.ring_cols(), term.ring_srow(), term.ring_erow() );
// dbg!(term.hist_srow(), term.hist_erow(), term.hist_rows(), ring_rows );
// dbg!(term.offset(), term.hist_use_srow(), term.hist_use() );
term.take_focus().unwrap();
term.clear_history();
assert_eq!(term.history_use(), 0);
// Subtract row numbers, modulo `rows`
fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
match a - b {
n if n < 0 => n + rows,
n => n
}
}
// disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert!(term.disp_srow() >= 0);
assert!(term.disp_erow() >= 0);
assert!(term.hist_srow() >= 0);
assert!(term.hist_erow() >= 0);
assert!(term.offset() >= 0);
assert!(term.disp_srow() <= ring_rows);
assert!(term.disp_erow() <= ring_rows);
assert!(term.hist_srow() <= ring_rows);
assert!(term.hist_erow() <= ring_rows);
assert!(term.offset() <= ring_rows);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
assert_eq!(term.ring_erow(), term.ring_rows() - 1);
assert_eq!(term.ring_srow(), 0);
// Check the different cols methods, which should all return the same answer
assert!(term.disp_cols() > 10);
assert_eq!(term.disp_cols(), term.ring_cols());
assert_eq!(term.disp_cols(), term.hist_cols());
// Redundant protected vs public methods:
assert_eq!(term.disp_cols(), term.display_columns());
assert_eq!(term.disp_rows(), term.display_rows());
/// Local function to read back all rows from the display into a long string.
/// Does not include scrollback history.
/// Trims trailing blanks on each line
fn read_disp(term: &Terminal) -> String {
let rows = term.display_rows();
let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
for row in 0..rows {
let r = term.u8c_disp_row(row).trim();
// Iterate through a row, accumulating [u8]
for c in r.iter() {
// Note: Sometimes utf-8 length is > 1
text.extend_from_slice(c.text_utf8());
}
text.extend_from_slice(b"\n");
}
// Return the result as a string
std::str::from_utf8(&text).unwrap().to_string()
}
term.clear();
term.append("Top line ↑ (up-arrow)");
term.set_text_attrib(Attrib::Underline);
term.append(" ");
term.set_text_attrib(Attrib::Normal);
term.append(" \n");
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
// The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
assert_eq!(text_out, "Top line ↑ (up-arrow) \n");
let r = term.u8c_disp_row(0);
assert_eq!(r.col(0).text_utf8(), b"T");
assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
let r = term.u8c_disp_row(1);
assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
// Clear the screen again, then append test text, then read it back and compare
let test_text =
"The wind was a torrent of darkness among the gusty trees.
The moon was a ghostly galleon tossed upon cloudy seas.
The road was a ribbon of moonlight over the purple moor,
And the highwayman came riding—
Riding—riding—
The highwayman came riding, up to the old inn-door.";
term.clear_history();
term.clear();
let bg_save = term.text_bg_color();
let fg_save = term.text_fg_color();
term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
term.append(test_text);
term.set_text_bg_color(bg_save);
term.set_text_fg_color(fg_save);
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
assert_eq!(test_text, text_out);
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
term.append(&format!(
"\n\nScreen has {} rows of {} columns.\n",
term.disp_rows(),
term.disp_cols()
));
term.set_text_attrib(Attrib::Italic);
term.append("Done!");
term.set_text_attrib(Attrib::Normal);
}
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?
446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502
fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let hist = term.history_use();
assert_ne!(hist, 0);
term.clear();
assert_eq!(term.cursor_row(), 0);
assert_eq!(term.history_use(), hist + term.display_rows()); // A screenful of lines added to history
term.append("Test\ntext\na\nb\nc\nd");
assert_eq!(term.cursor_row(), 5);
let hist = term.history_use();
term.clear_screen_home(false);
assert_eq!(term.cursor_row(), 0);
assert_eq!(term.history_use(), hist); // History not changed
term.append("Test\ntext\na\nb\nc\nd\ne");
assert_eq!(term.cursor_row(), 6);
term.clear_screen_home(true);
assert_eq!(term.cursor_row(), 0);
let hist = term.history_use();
term.append("Test\ntext\na\nb\nc\n");
assert_eq!(term.cursor_row(), 5);
term.clear_to_color(Color::DarkBlue);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
assert!(term.history_use() > hist); // Some lines added to history
assert_eq!(term.cursor_row(), 0);
// Test cursor_home()
term.append("Test\n\n\n\n\n\n\n\n\n\n");
assert_eq!(term.cursor_row(), 10);
term.cursor_home();
assert_eq!(term.cursor_row(), 0);
// Test the widget color
assert_eq!(term.color(), Color::Black); // Default
term.set_color(Color::DarkGreen);
assert_eq!(term.color(), Color::DarkGreen);
term.set_color(Color::Black);
assert_eq!(term.color(), Color::Black);
term.append(
"This should be one line of white text on black, embedded into the top of a blue field.\n",
);
assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
term.set_output_translate(OutFlags::OFF);
assert_eq!(term.output_translate(), OutFlags::OFF);
term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nDone!\n");
term.set_text_attrib(Attrib::Normal);
}
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?
446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502
fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let hist = term.history_use();
assert_ne!(hist, 0);
term.clear();
assert_eq!(term.cursor_row(), 0);
assert_eq!(term.history_use(), hist + term.display_rows()); // A screenful of lines added to history
term.append("Test\ntext\na\nb\nc\nd");
assert_eq!(term.cursor_row(), 5);
let hist = term.history_use();
term.clear_screen_home(false);
assert_eq!(term.cursor_row(), 0);
assert_eq!(term.history_use(), hist); // History not changed
term.append("Test\ntext\na\nb\nc\nd\ne");
assert_eq!(term.cursor_row(), 6);
term.clear_screen_home(true);
assert_eq!(term.cursor_row(), 0);
let hist = term.history_use();
term.append("Test\ntext\na\nb\nc\n");
assert_eq!(term.cursor_row(), 5);
term.clear_to_color(Color::DarkBlue);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
assert!(term.history_use() > hist); // Some lines added to history
assert_eq!(term.cursor_row(), 0);
// Test cursor_home()
term.append("Test\n\n\n\n\n\n\n\n\n\n");
assert_eq!(term.cursor_row(), 10);
term.cursor_home();
assert_eq!(term.cursor_row(), 0);
// Test the widget color
assert_eq!(term.color(), Color::Black); // Default
term.set_color(Color::DarkGreen);
assert_eq!(term.color(), Color::DarkGreen);
term.set_color(Color::Black);
assert_eq!(term.color(), Color::Black);
term.append(
"This should be one line of white text on black, embedded into the top of a blue field.\n",
);
assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
term.set_output_translate(OutFlags::OFF);
assert_eq!(term.output_translate(), OutFlags::OFF);
term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nDone!\n");
term.set_text_attrib(Attrib::Normal);
}
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?
423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
term.reset_terminal();
for i in 0..50 {
term.append(&format!("{i}\n"));
}
assert_eq!(term.cursor_row(), 30);
assert_eq!(term.display_rows(), 31);
assert_eq!(term.history_rows(), 100);
assert_eq!(term.history_lines(), 100);
assert_eq!(term.history_use(), 20);
term.clear_history();
assert_eq!(term.history_use(), 0);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nDone!\n");
term.set_text_attrib(Attrib::Normal);
}
//--------------------------------------------------------------------------------------
/// Another set of tests that run when Test3 is clicked
fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let hist = term.history_use();
assert_ne!(hist, 0);
term.clear();
assert_eq!(term.cursor_row(), 0);
assert_eq!(term.history_use(), hist + term.display_rows()); // A screenful of lines added to history
term.append("Test\ntext\na\nb\nc\nd");
assert_eq!(term.cursor_row(), 5);
let hist = term.history_use();
term.clear_screen_home(false);
assert_eq!(term.cursor_row(), 0);
assert_eq!(term.history_use(), hist); // History not changed
term.append("Test\ntext\na\nb\nc\nd\ne");
assert_eq!(term.cursor_row(), 6);
term.clear_screen_home(true);
assert_eq!(term.cursor_row(), 0);
let hist = term.history_use();
term.append("Test\ntext\na\nb\nc\n");
assert_eq!(term.cursor_row(), 5);
term.clear_to_color(Color::DarkBlue);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
assert!(term.history_use() > hist); // Some lines added to history
assert_eq!(term.cursor_row(), 0);
// Test cursor_home()
term.append("Test\n\n\n\n\n\n\n\n\n\n");
assert_eq!(term.cursor_row(), 10);
term.cursor_home();
assert_eq!(term.cursor_row(), 0);
// Test the widget color
assert_eq!(term.color(), Color::Black); // Default
term.set_color(Color::DarkGreen);
assert_eq!(term.color(), Color::DarkGreen);
term.set_color(Color::Black);
assert_eq!(term.color(), Color::Black);
term.append(
"This should be one line of white text on black, embedded into the top of a blue field.\n",
);
assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
term.set_output_translate(OutFlags::OFF);
assert_eq!(term.output_translate(), OutFlags::OFF);
term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nDone!\n");
term.set_text_attrib(Attrib::Normal);
}
//--------------------------------------------------------------------------------------
/// Another set of tests for the ring-buffer access methods
fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
// Test the Utf8Char primitive
let uc = Utf8Char::new(b'Q');
let uc1 = uc.text_utf8();
assert_eq!(&uc1, &[b'Q']);
assert_eq!(&uc.attrib(), &Attrib::Normal);
assert_eq!(
&uc.charflags(),
&(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
);
assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
let ring_rows = term.ring_rows();
// println!();
// dbg!(term.disp_srow(), term.disp_erow(), term.disp_rows(), term.ring_cols(), term.ring_srow(), term.ring_erow() );
// dbg!(term.hist_srow(), term.hist_erow(), term.hist_rows(), ring_rows );
// dbg!(term.offset(), term.hist_use_srow(), term.hist_use() );
term.take_focus().unwrap();
term.clear_history();
assert_eq!(term.history_use(), 0);
// Subtract row numbers, modulo `rows`
fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
match a - b {
n if n < 0 => n + rows,
n => n
}
}
// disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert!(term.disp_srow() >= 0);
assert!(term.disp_erow() >= 0);
assert!(term.hist_srow() >= 0);
assert!(term.hist_erow() >= 0);
assert!(term.offset() >= 0);
assert!(term.disp_srow() <= ring_rows);
assert!(term.disp_erow() <= ring_rows);
assert!(term.hist_srow() <= ring_rows);
assert!(term.hist_erow() <= ring_rows);
assert!(term.offset() <= ring_rows);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
assert_eq!(term.ring_erow(), term.ring_rows() - 1);
assert_eq!(term.ring_srow(), 0);
// Check the different cols methods, which should all return the same answer
assert!(term.disp_cols() > 10);
assert_eq!(term.disp_cols(), term.ring_cols());
assert_eq!(term.disp_cols(), term.hist_cols());
// Redundant protected vs public methods:
assert_eq!(term.disp_cols(), term.display_columns());
assert_eq!(term.disp_rows(), term.display_rows());
/// Local function to read back all rows from the display into a long string.
/// Does not include scrollback history.
/// Trims trailing blanks on each line
fn read_disp(term: &Terminal) -> String {
let rows = term.display_rows();
let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
for row in 0..rows {
let r = term.u8c_disp_row(row).trim();
// Iterate through a row, accumulating [u8]
for c in r.iter() {
// Note: Sometimes utf-8 length is > 1
text.extend_from_slice(c.text_utf8());
}
text.extend_from_slice(b"\n");
}
// Return the result as a string
std::str::from_utf8(&text).unwrap().to_string()
}
term.clear();
term.append("Top line ↑ (up-arrow)");
term.set_text_attrib(Attrib::Underline);
term.append(" ");
term.set_text_attrib(Attrib::Normal);
term.append(" \n");
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
// The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
assert_eq!(text_out, "Top line ↑ (up-arrow) \n");
let r = term.u8c_disp_row(0);
assert_eq!(r.col(0).text_utf8(), b"T");
assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
let r = term.u8c_disp_row(1);
assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
// Clear the screen again, then append test text, then read it back and compare
let test_text =
"The wind was a torrent of darkness among the gusty trees.
The moon was a ghostly galleon tossed upon cloudy seas.
The road was a ribbon of moonlight over the purple moor,
And the highwayman came riding—
Riding—riding—
The highwayman came riding, up to the old inn-door.";
term.clear_history();
term.clear();
let bg_save = term.text_bg_color();
let fg_save = term.text_fg_color();
term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
term.append(test_text);
term.set_text_bg_color(bg_save);
term.set_text_fg_color(fg_save);
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
assert_eq!(test_text, text_out);
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
term.append(&format!(
"\n\nScreen has {} rows of {} columns.\n",
term.disp_rows(),
term.disp_cols()
));
term.set_text_attrib(Attrib::Italic);
term.append("Done!");
term.set_text_attrib(Attrib::Normal);
}
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?
446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502
fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let hist = term.history_use();
assert_ne!(hist, 0);
term.clear();
assert_eq!(term.cursor_row(), 0);
assert_eq!(term.history_use(), hist + term.display_rows()); // A screenful of lines added to history
term.append("Test\ntext\na\nb\nc\nd");
assert_eq!(term.cursor_row(), 5);
let hist = term.history_use();
term.clear_screen_home(false);
assert_eq!(term.cursor_row(), 0);
assert_eq!(term.history_use(), hist); // History not changed
term.append("Test\ntext\na\nb\nc\nd\ne");
assert_eq!(term.cursor_row(), 6);
term.clear_screen_home(true);
assert_eq!(term.cursor_row(), 0);
let hist = term.history_use();
term.append("Test\ntext\na\nb\nc\n");
assert_eq!(term.cursor_row(), 5);
term.clear_to_color(Color::DarkBlue);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
assert!(term.history_use() > hist); // Some lines added to history
assert_eq!(term.cursor_row(), 0);
// Test cursor_home()
term.append("Test\n\n\n\n\n\n\n\n\n\n");
assert_eq!(term.cursor_row(), 10);
term.cursor_home();
assert_eq!(term.cursor_row(), 0);
// Test the widget color
assert_eq!(term.color(), Color::Black); // Default
term.set_color(Color::DarkGreen);
assert_eq!(term.color(), Color::DarkGreen);
term.set_color(Color::Black);
assert_eq!(term.color(), Color::Black);
term.append(
"This should be one line of white text on black, embedded into the top of a blue field.\n",
);
assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
term.set_output_translate(OutFlags::OFF);
assert_eq!(term.output_translate(), OutFlags::OFF);
term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nDone!\n");
term.set_text_attrib(Attrib::Normal);
}
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?
446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502
fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let hist = term.history_use();
assert_ne!(hist, 0);
term.clear();
assert_eq!(term.cursor_row(), 0);
assert_eq!(term.history_use(), hist + term.display_rows()); // A screenful of lines added to history
term.append("Test\ntext\na\nb\nc\nd");
assert_eq!(term.cursor_row(), 5);
let hist = term.history_use();
term.clear_screen_home(false);
assert_eq!(term.cursor_row(), 0);
assert_eq!(term.history_use(), hist); // History not changed
term.append("Test\ntext\na\nb\nc\nd\ne");
assert_eq!(term.cursor_row(), 6);
term.clear_screen_home(true);
assert_eq!(term.cursor_row(), 0);
let hist = term.history_use();
term.append("Test\ntext\na\nb\nc\n");
assert_eq!(term.cursor_row(), 5);
term.clear_to_color(Color::DarkBlue);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
assert!(term.history_use() > hist); // Some lines added to history
assert_eq!(term.cursor_row(), 0);
// Test cursor_home()
term.append("Test\n\n\n\n\n\n\n\n\n\n");
assert_eq!(term.cursor_row(), 10);
term.cursor_home();
assert_eq!(term.cursor_row(), 0);
// Test the widget color
assert_eq!(term.color(), Color::Black); // Default
term.set_color(Color::DarkGreen);
assert_eq!(term.color(), Color::DarkGreen);
term.set_color(Color::Black);
assert_eq!(term.color(), Color::Black);
term.append(
"This should be one line of white text on black, embedded into the top of a blue field.\n",
);
assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
term.set_output_translate(OutFlags::OFF);
assert_eq!(term.output_translate(), OutFlags::OFF);
term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nDone!\n");
term.set_text_attrib(Attrib::Normal);
}
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?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
//--------------------------------------------------------------------------------------
/// More tests that run when the menu bar Test1 is clicked
fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
term.reset_terminal();
term.append("0123456789 0\n");
term.append("0123456789 1\n");
term.append("0123456789 2\n");
term.append("0123456789 3\n");
term.append("0123456789 4\n");
term.append("0123456789 5\n");
term.append("0123456789 6\n");
term.append("0123456789 7\n");
term.append("0123456789 8\n");
term.append("0123456789 9\n");
term.append("------------\n");
term.set_text_fg_color(Color::Green);
term.put_char('A', 0, 0);
term.put_char('B', 1, 1);
term.put_char('C', 2, 2);
term.put_char('D', 3, 3);
term.put_char('E', 4, 4);
term.put_char('F', 5, 5);
term.set_text_fg_color(Color::XtermWhite);
assert_eq!(term.cursor_row(), 11);
assert_eq!(term.cursor_col(), 0);
term.set_text_bg_color(Color::DarkBlue);
term.put_char_utf8('b', 8, 1);
term.put_char_utf8('↑', 9, 1);
term.put_char_utf8('c', 8, 2);
term.put_char_utf8('↑', 9, 2);
term.put_char_utf8('d', 8, 3);
term.put_char_utf8('↑', 9, 3);
term.put_char_utf8('e', 8, 4);
term.put_char_utf8('↑', 9, 4);
term.put_char_utf8('f', 8, 5);
term.put_char_utf8('↑', 9, 5);
term.put_char_utf8('g', 8, 6);
term.put_char_utf8('↑', 9, 6);
term.set_text_bg_color(Color::Black);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("Done!\n");
term.set_text_attrib(Attrib::Normal);
}
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?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
//--------------------------------------------------------------------------------------
/// More tests that run when the menu bar Test1 is clicked
fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
term.reset_terminal();
term.append("0123456789 0\n");
term.append("0123456789 1\n");
term.append("0123456789 2\n");
term.append("0123456789 3\n");
term.append("0123456789 4\n");
term.append("0123456789 5\n");
term.append("0123456789 6\n");
term.append("0123456789 7\n");
term.append("0123456789 8\n");
term.append("0123456789 9\n");
term.append("------------\n");
term.set_text_fg_color(Color::Green);
term.put_char('A', 0, 0);
term.put_char('B', 1, 1);
term.put_char('C', 2, 2);
term.put_char('D', 3, 3);
term.put_char('E', 4, 4);
term.put_char('F', 5, 5);
term.set_text_fg_color(Color::XtermWhite);
assert_eq!(term.cursor_row(), 11);
assert_eq!(term.cursor_col(), 0);
term.set_text_bg_color(Color::DarkBlue);
term.put_char_utf8('b', 8, 1);
term.put_char_utf8('↑', 9, 1);
term.put_char_utf8('c', 8, 2);
term.put_char_utf8('↑', 9, 2);
term.put_char_utf8('d', 8, 3);
term.put_char_utf8('↑', 9, 3);
term.put_char_utf8('e', 8, 4);
term.put_char_utf8('↑', 9, 4);
term.put_char_utf8('f', 8, 5);
term.put_char_utf8('↑', 9, 5);
term.put_char_utf8('g', 8, 6);
term.put_char_utf8('↑', 9, 6);
term.set_text_bg_color(Color::Black);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("Done!\n");
term.set_text_attrib(Attrib::Normal);
}
//--------------------------------------------------------------------------------------
/// More tests that run when the menu bar button Test2 is clicked
fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
term.reset_terminal();
for i in 0..50 {
term.append(&format!("{i}\n"));
}
assert_eq!(term.cursor_row(), 30);
assert_eq!(term.display_rows(), 31);
assert_eq!(term.history_rows(), 100);
assert_eq!(term.history_lines(), 100);
assert_eq!(term.history_use(), 20);
term.clear_history();
assert_eq!(term.history_use(), 0);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nDone!\n");
term.set_text_attrib(Attrib::Normal);
}
//--------------------------------------------------------------------------------------
/// Another set of tests that run when Test3 is clicked
fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let hist = term.history_use();
assert_ne!(hist, 0);
term.clear();
assert_eq!(term.cursor_row(), 0);
assert_eq!(term.history_use(), hist + term.display_rows()); // A screenful of lines added to history
term.append("Test\ntext\na\nb\nc\nd");
assert_eq!(term.cursor_row(), 5);
let hist = term.history_use();
term.clear_screen_home(false);
assert_eq!(term.cursor_row(), 0);
assert_eq!(term.history_use(), hist); // History not changed
term.append("Test\ntext\na\nb\nc\nd\ne");
assert_eq!(term.cursor_row(), 6);
term.clear_screen_home(true);
assert_eq!(term.cursor_row(), 0);
let hist = term.history_use();
term.append("Test\ntext\na\nb\nc\n");
assert_eq!(term.cursor_row(), 5);
term.clear_to_color(Color::DarkBlue);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
assert!(term.history_use() > hist); // Some lines added to history
assert_eq!(term.cursor_row(), 0);
// Test cursor_home()
term.append("Test\n\n\n\n\n\n\n\n\n\n");
assert_eq!(term.cursor_row(), 10);
term.cursor_home();
assert_eq!(term.cursor_row(), 0);
// Test the widget color
assert_eq!(term.color(), Color::Black); // Default
term.set_color(Color::DarkGreen);
assert_eq!(term.color(), Color::DarkGreen);
term.set_color(Color::Black);
assert_eq!(term.color(), Color::Black);
term.append(
"This should be one line of white text on black, embedded into the top of a blue field.\n",
);
assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
term.set_output_translate(OutFlags::OFF);
assert_eq!(term.output_translate(), OutFlags::OFF);
term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nDone!\n");
term.set_text_attrib(Attrib::Normal);
}
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?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
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?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
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?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
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?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
sourcepub fn cursor_home(&mut self)
pub fn cursor_home(&mut self)
Move cursor to the home position (top/left).
Examples found in repository?
446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502
fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let hist = term.history_use();
assert_ne!(hist, 0);
term.clear();
assert_eq!(term.cursor_row(), 0);
assert_eq!(term.history_use(), hist + term.display_rows()); // A screenful of lines added to history
term.append("Test\ntext\na\nb\nc\nd");
assert_eq!(term.cursor_row(), 5);
let hist = term.history_use();
term.clear_screen_home(false);
assert_eq!(term.cursor_row(), 0);
assert_eq!(term.history_use(), hist); // History not changed
term.append("Test\ntext\na\nb\nc\nd\ne");
assert_eq!(term.cursor_row(), 6);
term.clear_screen_home(true);
assert_eq!(term.cursor_row(), 0);
let hist = term.history_use();
term.append("Test\ntext\na\nb\nc\n");
assert_eq!(term.cursor_row(), 5);
term.clear_to_color(Color::DarkBlue);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
assert!(term.history_use() > hist); // Some lines added to history
assert_eq!(term.cursor_row(), 0);
// Test cursor_home()
term.append("Test\n\n\n\n\n\n\n\n\n\n");
assert_eq!(term.cursor_row(), 10);
term.cursor_home();
assert_eq!(term.cursor_row(), 0);
// Test the widget color
assert_eq!(term.color(), Color::Black); // Default
term.set_color(Color::DarkGreen);
assert_eq!(term.color(), Color::DarkGreen);
term.set_color(Color::Black);
assert_eq!(term.color(), Color::Black);
term.append(
"This should be one line of white text on black, embedded into the top of a blue field.\n",
);
assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
term.set_output_translate(OutFlags::OFF);
assert_eq!(term.output_translate(), OutFlags::OFF);
term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nDone!\n");
term.set_text_attrib(Attrib::Normal);
}
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?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
//--------------------------------------------------------------------------------------
/// More tests that run when the menu bar Test1 is clicked
fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
term.reset_terminal();
term.append("0123456789 0\n");
term.append("0123456789 1\n");
term.append("0123456789 2\n");
term.append("0123456789 3\n");
term.append("0123456789 4\n");
term.append("0123456789 5\n");
term.append("0123456789 6\n");
term.append("0123456789 7\n");
term.append("0123456789 8\n");
term.append("0123456789 9\n");
term.append("------------\n");
term.set_text_fg_color(Color::Green);
term.put_char('A', 0, 0);
term.put_char('B', 1, 1);
term.put_char('C', 2, 2);
term.put_char('D', 3, 3);
term.put_char('E', 4, 4);
term.put_char('F', 5, 5);
term.set_text_fg_color(Color::XtermWhite);
assert_eq!(term.cursor_row(), 11);
assert_eq!(term.cursor_col(), 0);
term.set_text_bg_color(Color::DarkBlue);
term.put_char_utf8('b', 8, 1);
term.put_char_utf8('↑', 9, 1);
term.put_char_utf8('c', 8, 2);
term.put_char_utf8('↑', 9, 2);
term.put_char_utf8('d', 8, 3);
term.put_char_utf8('↑', 9, 3);
term.put_char_utf8('e', 8, 4);
term.put_char_utf8('↑', 9, 4);
term.put_char_utf8('f', 8, 5);
term.put_char_utf8('↑', 9, 5);
term.put_char_utf8('g', 8, 6);
term.put_char_utf8('↑', 9, 6);
term.set_text_bg_color(Color::Black);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("Done!\n");
term.set_text_attrib(Attrib::Normal);
}
//--------------------------------------------------------------------------------------
/// More tests that run when the menu bar button Test2 is clicked
fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
term.reset_terminal();
for i in 0..50 {
term.append(&format!("{i}\n"));
}
assert_eq!(term.cursor_row(), 30);
assert_eq!(term.display_rows(), 31);
assert_eq!(term.history_rows(), 100);
assert_eq!(term.history_lines(), 100);
assert_eq!(term.history_use(), 20);
term.clear_history();
assert_eq!(term.history_use(), 0);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nDone!\n");
term.set_text_attrib(Attrib::Normal);
}
//--------------------------------------------------------------------------------------
/// Another set of tests that run when Test3 is clicked
fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let hist = term.history_use();
assert_ne!(hist, 0);
term.clear();
assert_eq!(term.cursor_row(), 0);
assert_eq!(term.history_use(), hist + term.display_rows()); // A screenful of lines added to history
term.append("Test\ntext\na\nb\nc\nd");
assert_eq!(term.cursor_row(), 5);
let hist = term.history_use();
term.clear_screen_home(false);
assert_eq!(term.cursor_row(), 0);
assert_eq!(term.history_use(), hist); // History not changed
term.append("Test\ntext\na\nb\nc\nd\ne");
assert_eq!(term.cursor_row(), 6);
term.clear_screen_home(true);
assert_eq!(term.cursor_row(), 0);
let hist = term.history_use();
term.append("Test\ntext\na\nb\nc\n");
assert_eq!(term.cursor_row(), 5);
term.clear_to_color(Color::DarkBlue);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
assert!(term.history_use() > hist); // Some lines added to history
assert_eq!(term.cursor_row(), 0);
// Test cursor_home()
term.append("Test\n\n\n\n\n\n\n\n\n\n");
assert_eq!(term.cursor_row(), 10);
term.cursor_home();
assert_eq!(term.cursor_row(), 0);
// Test the widget color
assert_eq!(term.color(), Color::Black); // Default
term.set_color(Color::DarkGreen);
assert_eq!(term.color(), Color::DarkGreen);
term.set_color(Color::Black);
assert_eq!(term.color(), Color::Black);
term.append(
"This should be one line of white text on black, embedded into the top of a blue field.\n",
);
assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
term.set_output_translate(OutFlags::OFF);
assert_eq!(term.output_translate(), OutFlags::OFF);
term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nDone!\n");
term.set_text_attrib(Attrib::Normal);
}
//--------------------------------------------------------------------------------------
/// Another set of tests for the ring-buffer access methods
fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
// Test the Utf8Char primitive
let uc = Utf8Char::new(b'Q');
let uc1 = uc.text_utf8();
assert_eq!(&uc1, &[b'Q']);
assert_eq!(&uc.attrib(), &Attrib::Normal);
assert_eq!(
&uc.charflags(),
&(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
);
assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
let ring_rows = term.ring_rows();
// println!();
// dbg!(term.disp_srow(), term.disp_erow(), term.disp_rows(), term.ring_cols(), term.ring_srow(), term.ring_erow() );
// dbg!(term.hist_srow(), term.hist_erow(), term.hist_rows(), ring_rows );
// dbg!(term.offset(), term.hist_use_srow(), term.hist_use() );
term.take_focus().unwrap();
term.clear_history();
assert_eq!(term.history_use(), 0);
// Subtract row numbers, modulo `rows`
fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
match a - b {
n if n < 0 => n + rows,
n => n
}
}
// disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert!(term.disp_srow() >= 0);
assert!(term.disp_erow() >= 0);
assert!(term.hist_srow() >= 0);
assert!(term.hist_erow() >= 0);
assert!(term.offset() >= 0);
assert!(term.disp_srow() <= ring_rows);
assert!(term.disp_erow() <= ring_rows);
assert!(term.hist_srow() <= ring_rows);
assert!(term.hist_erow() <= ring_rows);
assert!(term.offset() <= ring_rows);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
assert_eq!(term.ring_erow(), term.ring_rows() - 1);
assert_eq!(term.ring_srow(), 0);
// Check the different cols methods, which should all return the same answer
assert!(term.disp_cols() > 10);
assert_eq!(term.disp_cols(), term.ring_cols());
assert_eq!(term.disp_cols(), term.hist_cols());
// Redundant protected vs public methods:
assert_eq!(term.disp_cols(), term.display_columns());
assert_eq!(term.disp_rows(), term.display_rows());
/// Local function to read back all rows from the display into a long string.
/// Does not include scrollback history.
/// Trims trailing blanks on each line
fn read_disp(term: &Terminal) -> String {
let rows = term.display_rows();
let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
for row in 0..rows {
let r = term.u8c_disp_row(row).trim();
// Iterate through a row, accumulating [u8]
for c in r.iter() {
// Note: Sometimes utf-8 length is > 1
text.extend_from_slice(c.text_utf8());
}
text.extend_from_slice(b"\n");
}
// Return the result as a string
std::str::from_utf8(&text).unwrap().to_string()
}
term.clear();
term.append("Top line ↑ (up-arrow)");
term.set_text_attrib(Attrib::Underline);
term.append(" ");
term.set_text_attrib(Attrib::Normal);
term.append(" \n");
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
// The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
assert_eq!(text_out, "Top line ↑ (up-arrow) \n");
let r = term.u8c_disp_row(0);
assert_eq!(r.col(0).text_utf8(), b"T");
assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
let r = term.u8c_disp_row(1);
assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
// Clear the screen again, then append test text, then read it back and compare
let test_text =
"The wind was a torrent of darkness among the gusty trees.
The moon was a ghostly galleon tossed upon cloudy seas.
The road was a ribbon of moonlight over the purple moor,
And the highwayman came riding—
Riding—riding—
The highwayman came riding, up to the old inn-door.";
term.clear_history();
term.clear();
let bg_save = term.text_bg_color();
let fg_save = term.text_fg_color();
term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
term.append(test_text);
term.set_text_bg_color(bg_save);
term.set_text_fg_color(fg_save);
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
assert_eq!(test_text, text_out);
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
term.append(&format!(
"\n\nScreen has {} rows of {} columns.\n",
term.disp_rows(),
term.disp_cols()
));
term.set_text_attrib(Attrib::Italic);
term.append("Done!");
term.set_text_attrib(Attrib::Normal);
}
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.
Examples found in repository?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
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?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
//--------------------------------------------------------------------------------------
/// More tests that run when the menu bar Test1 is clicked
fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
term.reset_terminal();
term.append("0123456789 0\n");
term.append("0123456789 1\n");
term.append("0123456789 2\n");
term.append("0123456789 3\n");
term.append("0123456789 4\n");
term.append("0123456789 5\n");
term.append("0123456789 6\n");
term.append("0123456789 7\n");
term.append("0123456789 8\n");
term.append("0123456789 9\n");
term.append("------------\n");
term.set_text_fg_color(Color::Green);
term.put_char('A', 0, 0);
term.put_char('B', 1, 1);
term.put_char('C', 2, 2);
term.put_char('D', 3, 3);
term.put_char('E', 4, 4);
term.put_char('F', 5, 5);
term.set_text_fg_color(Color::XtermWhite);
assert_eq!(term.cursor_row(), 11);
assert_eq!(term.cursor_col(), 0);
term.set_text_bg_color(Color::DarkBlue);
term.put_char_utf8('b', 8, 1);
term.put_char_utf8('↑', 9, 1);
term.put_char_utf8('c', 8, 2);
term.put_char_utf8('↑', 9, 2);
term.put_char_utf8('d', 8, 3);
term.put_char_utf8('↑', 9, 3);
term.put_char_utf8('e', 8, 4);
term.put_char_utf8('↑', 9, 4);
term.put_char_utf8('f', 8, 5);
term.put_char_utf8('↑', 9, 5);
term.put_char_utf8('g', 8, 6);
term.put_char_utf8('↑', 9, 6);
term.set_text_bg_color(Color::Black);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("Done!\n");
term.set_text_attrib(Attrib::Normal);
}
//--------------------------------------------------------------------------------------
/// More tests that run when the menu bar button Test2 is clicked
fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
term.reset_terminal();
for i in 0..50 {
term.append(&format!("{i}\n"));
}
assert_eq!(term.cursor_row(), 30);
assert_eq!(term.display_rows(), 31);
assert_eq!(term.history_rows(), 100);
assert_eq!(term.history_lines(), 100);
assert_eq!(term.history_use(), 20);
term.clear_history();
assert_eq!(term.history_use(), 0);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nDone!\n");
term.set_text_attrib(Attrib::Normal);
}
//--------------------------------------------------------------------------------------
/// Another set of tests that run when Test3 is clicked
fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let hist = term.history_use();
assert_ne!(hist, 0);
term.clear();
assert_eq!(term.cursor_row(), 0);
assert_eq!(term.history_use(), hist + term.display_rows()); // A screenful of lines added to history
term.append("Test\ntext\na\nb\nc\nd");
assert_eq!(term.cursor_row(), 5);
let hist = term.history_use();
term.clear_screen_home(false);
assert_eq!(term.cursor_row(), 0);
assert_eq!(term.history_use(), hist); // History not changed
term.append("Test\ntext\na\nb\nc\nd\ne");
assert_eq!(term.cursor_row(), 6);
term.clear_screen_home(true);
assert_eq!(term.cursor_row(), 0);
let hist = term.history_use();
term.append("Test\ntext\na\nb\nc\n");
assert_eq!(term.cursor_row(), 5);
term.clear_to_color(Color::DarkBlue);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
assert!(term.history_use() > hist); // Some lines added to history
assert_eq!(term.cursor_row(), 0);
// Test cursor_home()
term.append("Test\n\n\n\n\n\n\n\n\n\n");
assert_eq!(term.cursor_row(), 10);
term.cursor_home();
assert_eq!(term.cursor_row(), 0);
// Test the widget color
assert_eq!(term.color(), Color::Black); // Default
term.set_color(Color::DarkGreen);
assert_eq!(term.color(), Color::DarkGreen);
term.set_color(Color::Black);
assert_eq!(term.color(), Color::Black);
term.append(
"This should be one line of white text on black, embedded into the top of a blue field.\n",
);
assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
term.set_output_translate(OutFlags::OFF);
assert_eq!(term.output_translate(), OutFlags::OFF);
term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nDone!\n");
term.set_text_attrib(Attrib::Normal);
}
//--------------------------------------------------------------------------------------
/// Another set of tests for the ring-buffer access methods
fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
// Test the Utf8Char primitive
let uc = Utf8Char::new(b'Q');
let uc1 = uc.text_utf8();
assert_eq!(&uc1, &[b'Q']);
assert_eq!(&uc.attrib(), &Attrib::Normal);
assert_eq!(
&uc.charflags(),
&(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
);
assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
let ring_rows = term.ring_rows();
// println!();
// dbg!(term.disp_srow(), term.disp_erow(), term.disp_rows(), term.ring_cols(), term.ring_srow(), term.ring_erow() );
// dbg!(term.hist_srow(), term.hist_erow(), term.hist_rows(), ring_rows );
// dbg!(term.offset(), term.hist_use_srow(), term.hist_use() );
term.take_focus().unwrap();
term.clear_history();
assert_eq!(term.history_use(), 0);
// Subtract row numbers, modulo `rows`
fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
match a - b {
n if n < 0 => n + rows,
n => n
}
}
// disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert!(term.disp_srow() >= 0);
assert!(term.disp_erow() >= 0);
assert!(term.hist_srow() >= 0);
assert!(term.hist_erow() >= 0);
assert!(term.offset() >= 0);
assert!(term.disp_srow() <= ring_rows);
assert!(term.disp_erow() <= ring_rows);
assert!(term.hist_srow() <= ring_rows);
assert!(term.hist_erow() <= ring_rows);
assert!(term.offset() <= ring_rows);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
assert_eq!(term.ring_erow(), term.ring_rows() - 1);
assert_eq!(term.ring_srow(), 0);
// Check the different cols methods, which should all return the same answer
assert!(term.disp_cols() > 10);
assert_eq!(term.disp_cols(), term.ring_cols());
assert_eq!(term.disp_cols(), term.hist_cols());
// Redundant protected vs public methods:
assert_eq!(term.disp_cols(), term.display_columns());
assert_eq!(term.disp_rows(), term.display_rows());
/// Local function to read back all rows from the display into a long string.
/// Does not include scrollback history.
/// Trims trailing blanks on each line
fn read_disp(term: &Terminal) -> String {
let rows = term.display_rows();
let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
for row in 0..rows {
let r = term.u8c_disp_row(row).trim();
// Iterate through a row, accumulating [u8]
for c in r.iter() {
// Note: Sometimes utf-8 length is > 1
text.extend_from_slice(c.text_utf8());
}
text.extend_from_slice(b"\n");
}
// Return the result as a string
std::str::from_utf8(&text).unwrap().to_string()
}
term.clear();
term.append("Top line ↑ (up-arrow)");
term.set_text_attrib(Attrib::Underline);
term.append(" ");
term.set_text_attrib(Attrib::Normal);
term.append(" \n");
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
// The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
assert_eq!(text_out, "Top line ↑ (up-arrow) \n");
let r = term.u8c_disp_row(0);
assert_eq!(r.col(0).text_utf8(), b"T");
assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
let r = term.u8c_disp_row(1);
assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
// Clear the screen again, then append test text, then read it back and compare
let test_text =
"The wind was a torrent of darkness among the gusty trees.
The moon was a ghostly galleon tossed upon cloudy seas.
The road was a ribbon of moonlight over the purple moor,
And the highwayman came riding—
Riding—riding—
The highwayman came riding, up to the old inn-door.";
term.clear_history();
term.clear();
let bg_save = term.text_bg_color();
let fg_save = term.text_fg_color();
term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
term.append(test_text);
term.set_text_bg_color(bg_save);
term.set_text_fg_color(fg_save);
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
assert_eq!(test_text, text_out);
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
term.append(&format!(
"\n\nScreen has {} rows of {} columns.\n",
term.disp_rows(),
term.disp_cols()
));
term.set_text_attrib(Attrib::Italic);
term.append("Done!");
term.set_text_attrib(Attrib::Normal);
}
sourcepub fn set_display_rows(&mut self, val: i32)
pub fn set_display_rows(&mut self, val: i32)
Set terminal’s display height in lines of text.
Examples found in repository?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
sourcepub fn set_history_lines(&mut self, arg1: i32)
pub fn set_history_lines(&mut self, arg1: i32)
Sets the number of lines of screen history.
Examples found in repository?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
sourcepub fn history_lines(&self) -> i32
pub fn history_lines(&self) -> i32
Gets the number of lines of screen history.
Examples found in repository?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
//--------------------------------------------------------------------------------------
/// More tests that run when the menu bar Test1 is clicked
fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
term.reset_terminal();
term.append("0123456789 0\n");
term.append("0123456789 1\n");
term.append("0123456789 2\n");
term.append("0123456789 3\n");
term.append("0123456789 4\n");
term.append("0123456789 5\n");
term.append("0123456789 6\n");
term.append("0123456789 7\n");
term.append("0123456789 8\n");
term.append("0123456789 9\n");
term.append("------------\n");
term.set_text_fg_color(Color::Green);
term.put_char('A', 0, 0);
term.put_char('B', 1, 1);
term.put_char('C', 2, 2);
term.put_char('D', 3, 3);
term.put_char('E', 4, 4);
term.put_char('F', 5, 5);
term.set_text_fg_color(Color::XtermWhite);
assert_eq!(term.cursor_row(), 11);
assert_eq!(term.cursor_col(), 0);
term.set_text_bg_color(Color::DarkBlue);
term.put_char_utf8('b', 8, 1);
term.put_char_utf8('↑', 9, 1);
term.put_char_utf8('c', 8, 2);
term.put_char_utf8('↑', 9, 2);
term.put_char_utf8('d', 8, 3);
term.put_char_utf8('↑', 9, 3);
term.put_char_utf8('e', 8, 4);
term.put_char_utf8('↑', 9, 4);
term.put_char_utf8('f', 8, 5);
term.put_char_utf8('↑', 9, 5);
term.put_char_utf8('g', 8, 6);
term.put_char_utf8('↑', 9, 6);
term.set_text_bg_color(Color::Black);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("Done!\n");
term.set_text_attrib(Attrib::Normal);
}
//--------------------------------------------------------------------------------------
/// More tests that run when the menu bar button Test2 is clicked
fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
term.reset_terminal();
for i in 0..50 {
term.append(&format!("{i}\n"));
}
assert_eq!(term.cursor_row(), 30);
assert_eq!(term.display_rows(), 31);
assert_eq!(term.history_rows(), 100);
assert_eq!(term.history_lines(), 100);
assert_eq!(term.history_use(), 20);
term.clear_history();
assert_eq!(term.history_use(), 0);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nDone!\n");
term.set_text_attrib(Attrib::Normal);
}
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?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
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?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
//--------------------------------------------------------------------------------------
/// More tests that run when the menu bar Test1 is clicked
fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
term.reset_terminal();
term.append("0123456789 0\n");
term.append("0123456789 1\n");
term.append("0123456789 2\n");
term.append("0123456789 3\n");
term.append("0123456789 4\n");
term.append("0123456789 5\n");
term.append("0123456789 6\n");
term.append("0123456789 7\n");
term.append("0123456789 8\n");
term.append("0123456789 9\n");
term.append("------------\n");
term.set_text_fg_color(Color::Green);
term.put_char('A', 0, 0);
term.put_char('B', 1, 1);
term.put_char('C', 2, 2);
term.put_char('D', 3, 3);
term.put_char('E', 4, 4);
term.put_char('F', 5, 5);
term.set_text_fg_color(Color::XtermWhite);
assert_eq!(term.cursor_row(), 11);
assert_eq!(term.cursor_col(), 0);
term.set_text_bg_color(Color::DarkBlue);
term.put_char_utf8('b', 8, 1);
term.put_char_utf8('↑', 9, 1);
term.put_char_utf8('c', 8, 2);
term.put_char_utf8('↑', 9, 2);
term.put_char_utf8('d', 8, 3);
term.put_char_utf8('↑', 9, 3);
term.put_char_utf8('e', 8, 4);
term.put_char_utf8('↑', 9, 4);
term.put_char_utf8('f', 8, 5);
term.put_char_utf8('↑', 9, 5);
term.put_char_utf8('g', 8, 6);
term.put_char_utf8('↑', 9, 6);
term.set_text_bg_color(Color::Black);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("Done!\n");
term.set_text_attrib(Attrib::Normal);
}
//--------------------------------------------------------------------------------------
/// More tests that run when the menu bar button Test2 is clicked
fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
term.reset_terminal();
for i in 0..50 {
term.append(&format!("{i}\n"));
}
assert_eq!(term.cursor_row(), 30);
assert_eq!(term.display_rows(), 31);
assert_eq!(term.history_rows(), 100);
assert_eq!(term.history_lines(), 100);
assert_eq!(term.history_use(), 20);
term.clear_history();
assert_eq!(term.history_use(), 0);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nDone!\n");
term.set_text_attrib(Attrib::Normal);
}
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?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
//--------------------------------------------------------------------------------------
/// More tests that run when the menu bar Test1 is clicked
fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
term.reset_terminal();
term.append("0123456789 0\n");
term.append("0123456789 1\n");
term.append("0123456789 2\n");
term.append("0123456789 3\n");
term.append("0123456789 4\n");
term.append("0123456789 5\n");
term.append("0123456789 6\n");
term.append("0123456789 7\n");
term.append("0123456789 8\n");
term.append("0123456789 9\n");
term.append("------------\n");
term.set_text_fg_color(Color::Green);
term.put_char('A', 0, 0);
term.put_char('B', 1, 1);
term.put_char('C', 2, 2);
term.put_char('D', 3, 3);
term.put_char('E', 4, 4);
term.put_char('F', 5, 5);
term.set_text_fg_color(Color::XtermWhite);
assert_eq!(term.cursor_row(), 11);
assert_eq!(term.cursor_col(), 0);
term.set_text_bg_color(Color::DarkBlue);
term.put_char_utf8('b', 8, 1);
term.put_char_utf8('↑', 9, 1);
term.put_char_utf8('c', 8, 2);
term.put_char_utf8('↑', 9, 2);
term.put_char_utf8('d', 8, 3);
term.put_char_utf8('↑', 9, 3);
term.put_char_utf8('e', 8, 4);
term.put_char_utf8('↑', 9, 4);
term.put_char_utf8('f', 8, 5);
term.put_char_utf8('↑', 9, 5);
term.put_char_utf8('g', 8, 6);
term.put_char_utf8('↑', 9, 6);
term.set_text_bg_color(Color::Black);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("Done!\n");
term.set_text_attrib(Attrib::Normal);
}
//--------------------------------------------------------------------------------------
/// More tests that run when the menu bar button Test2 is clicked
fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
term.reset_terminal();
for i in 0..50 {
term.append(&format!("{i}\n"));
}
assert_eq!(term.cursor_row(), 30);
assert_eq!(term.display_rows(), 31);
assert_eq!(term.history_rows(), 100);
assert_eq!(term.history_lines(), 100);
assert_eq!(term.history_use(), 20);
term.clear_history();
assert_eq!(term.history_use(), 0);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nDone!\n");
term.set_text_attrib(Attrib::Normal);
}
//--------------------------------------------------------------------------------------
/// Another set of tests that run when Test3 is clicked
fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let hist = term.history_use();
assert_ne!(hist, 0);
term.clear();
assert_eq!(term.cursor_row(), 0);
assert_eq!(term.history_use(), hist + term.display_rows()); // A screenful of lines added to history
term.append("Test\ntext\na\nb\nc\nd");
assert_eq!(term.cursor_row(), 5);
let hist = term.history_use();
term.clear_screen_home(false);
assert_eq!(term.cursor_row(), 0);
assert_eq!(term.history_use(), hist); // History not changed
term.append("Test\ntext\na\nb\nc\nd\ne");
assert_eq!(term.cursor_row(), 6);
term.clear_screen_home(true);
assert_eq!(term.cursor_row(), 0);
let hist = term.history_use();
term.append("Test\ntext\na\nb\nc\n");
assert_eq!(term.cursor_row(), 5);
term.clear_to_color(Color::DarkBlue);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
assert!(term.history_use() > hist); // Some lines added to history
assert_eq!(term.cursor_row(), 0);
// Test cursor_home()
term.append("Test\n\n\n\n\n\n\n\n\n\n");
assert_eq!(term.cursor_row(), 10);
term.cursor_home();
assert_eq!(term.cursor_row(), 0);
// Test the widget color
assert_eq!(term.color(), Color::Black); // Default
term.set_color(Color::DarkGreen);
assert_eq!(term.color(), Color::DarkGreen);
term.set_color(Color::Black);
assert_eq!(term.color(), Color::Black);
term.append(
"This should be one line of white text on black, embedded into the top of a blue field.\n",
);
assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
term.set_output_translate(OutFlags::OFF);
assert_eq!(term.output_translate(), OutFlags::OFF);
term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nDone!\n");
term.set_text_attrib(Attrib::Normal);
}
//--------------------------------------------------------------------------------------
/// Another set of tests for the ring-buffer access methods
fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
// Test the Utf8Char primitive
let uc = Utf8Char::new(b'Q');
let uc1 = uc.text_utf8();
assert_eq!(&uc1, &[b'Q']);
assert_eq!(&uc.attrib(), &Attrib::Normal);
assert_eq!(
&uc.charflags(),
&(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
);
assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
let ring_rows = term.ring_rows();
// println!();
// dbg!(term.disp_srow(), term.disp_erow(), term.disp_rows(), term.ring_cols(), term.ring_srow(), term.ring_erow() );
// dbg!(term.hist_srow(), term.hist_erow(), term.hist_rows(), ring_rows );
// dbg!(term.offset(), term.hist_use_srow(), term.hist_use() );
term.take_focus().unwrap();
term.clear_history();
assert_eq!(term.history_use(), 0);
// Subtract row numbers, modulo `rows`
fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
match a - b {
n if n < 0 => n + rows,
n => n
}
}
// disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert!(term.disp_srow() >= 0);
assert!(term.disp_erow() >= 0);
assert!(term.hist_srow() >= 0);
assert!(term.hist_erow() >= 0);
assert!(term.offset() >= 0);
assert!(term.disp_srow() <= ring_rows);
assert!(term.disp_erow() <= ring_rows);
assert!(term.hist_srow() <= ring_rows);
assert!(term.hist_erow() <= ring_rows);
assert!(term.offset() <= ring_rows);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
assert_eq!(term.ring_erow(), term.ring_rows() - 1);
assert_eq!(term.ring_srow(), 0);
// Check the different cols methods, which should all return the same answer
assert!(term.disp_cols() > 10);
assert_eq!(term.disp_cols(), term.ring_cols());
assert_eq!(term.disp_cols(), term.hist_cols());
// Redundant protected vs public methods:
assert_eq!(term.disp_cols(), term.display_columns());
assert_eq!(term.disp_rows(), term.display_rows());
/// Local function to read back all rows from the display into a long string.
/// Does not include scrollback history.
/// Trims trailing blanks on each line
fn read_disp(term: &Terminal) -> String {
let rows = term.display_rows();
let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
for row in 0..rows {
let r = term.u8c_disp_row(row).trim();
// Iterate through a row, accumulating [u8]
for c in r.iter() {
// Note: Sometimes utf-8 length is > 1
text.extend_from_slice(c.text_utf8());
}
text.extend_from_slice(b"\n");
}
// Return the result as a string
std::str::from_utf8(&text).unwrap().to_string()
}
term.clear();
term.append("Top line ↑ (up-arrow)");
term.set_text_attrib(Attrib::Underline);
term.append(" ");
term.set_text_attrib(Attrib::Normal);
term.append(" \n");
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
// The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
assert_eq!(text_out, "Top line ↑ (up-arrow) \n");
let r = term.u8c_disp_row(0);
assert_eq!(r.col(0).text_utf8(), b"T");
assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
let r = term.u8c_disp_row(1);
assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
// Clear the screen again, then append test text, then read it back and compare
let test_text =
"The wind was a torrent of darkness among the gusty trees.
The moon was a ghostly galleon tossed upon cloudy seas.
The road was a ribbon of moonlight over the purple moor,
And the highwayman came riding—
Riding—riding—
The highwayman came riding, up to the old inn-door.";
term.clear_history();
term.clear();
let bg_save = term.text_bg_color();
let fg_save = term.text_fg_color();
term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
term.append(test_text);
term.set_text_bg_color(bg_save);
term.set_text_fg_color(fg_save);
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
assert_eq!(test_text, text_out);
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
term.append(&format!(
"\n\nScreen has {} rows of {} columns.\n",
term.disp_rows(),
term.disp_cols()
));
term.set_text_attrib(Attrib::Italic);
term.append("Done!");
term.set_text_attrib(Attrib::Normal);
}
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?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
sourcepub fn margin_bottom(&self) -> i32
pub fn margin_bottom(&self) -> i32
Return the bottom margin
Examples found in repository?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
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?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
sourcepub fn margin_left(&self) -> i32
pub fn margin_left(&self) -> i32
Return the left margin
Examples found in repository?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
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?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
sourcepub fn margin_right(&self) -> i32
pub fn margin_right(&self) -> i32
Return the right margin
Examples found in repository?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
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?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
sourcepub fn margin_top(&self) -> i32
pub fn margin_top(&self) -> i32
Return the top margin
Examples found in repository?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
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?
446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502
fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let hist = term.history_use();
assert_ne!(hist, 0);
term.clear();
assert_eq!(term.cursor_row(), 0);
assert_eq!(term.history_use(), hist + term.display_rows()); // A screenful of lines added to history
term.append("Test\ntext\na\nb\nc\nd");
assert_eq!(term.cursor_row(), 5);
let hist = term.history_use();
term.clear_screen_home(false);
assert_eq!(term.cursor_row(), 0);
assert_eq!(term.history_use(), hist); // History not changed
term.append("Test\ntext\na\nb\nc\nd\ne");
assert_eq!(term.cursor_row(), 6);
term.clear_screen_home(true);
assert_eq!(term.cursor_row(), 0);
let hist = term.history_use();
term.append("Test\ntext\na\nb\nc\n");
assert_eq!(term.cursor_row(), 5);
term.clear_to_color(Color::DarkBlue);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
assert!(term.history_use() > hist); // Some lines added to history
assert_eq!(term.cursor_row(), 0);
// Test cursor_home()
term.append("Test\n\n\n\n\n\n\n\n\n\n");
assert_eq!(term.cursor_row(), 10);
term.cursor_home();
assert_eq!(term.cursor_row(), 0);
// Test the widget color
assert_eq!(term.color(), Color::Black); // Default
term.set_color(Color::DarkGreen);
assert_eq!(term.color(), Color::DarkGreen);
term.set_color(Color::Black);
assert_eq!(term.color(), Color::Black);
term.append(
"This should be one line of white text on black, embedded into the top of a blue field.\n",
);
assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
term.set_output_translate(OutFlags::OFF);
assert_eq!(term.output_translate(), OutFlags::OFF);
term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nDone!\n");
term.set_text_attrib(Attrib::Normal);
}
sourcepub fn output_translate(&self) -> OutFlags
pub fn output_translate(&self) -> OutFlags
Return the current combined output translation flags.
Examples found in repository?
446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502
fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let hist = term.history_use();
assert_ne!(hist, 0);
term.clear();
assert_eq!(term.cursor_row(), 0);
assert_eq!(term.history_use(), hist + term.display_rows()); // A screenful of lines added to history
term.append("Test\ntext\na\nb\nc\nd");
assert_eq!(term.cursor_row(), 5);
let hist = term.history_use();
term.clear_screen_home(false);
assert_eq!(term.cursor_row(), 0);
assert_eq!(term.history_use(), hist); // History not changed
term.append("Test\ntext\na\nb\nc\nd\ne");
assert_eq!(term.cursor_row(), 6);
term.clear_screen_home(true);
assert_eq!(term.cursor_row(), 0);
let hist = term.history_use();
term.append("Test\ntext\na\nb\nc\n");
assert_eq!(term.cursor_row(), 5);
term.clear_to_color(Color::DarkBlue);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
assert!(term.history_use() > hist); // Some lines added to history
assert_eq!(term.cursor_row(), 0);
// Test cursor_home()
term.append("Test\n\n\n\n\n\n\n\n\n\n");
assert_eq!(term.cursor_row(), 10);
term.cursor_home();
assert_eq!(term.cursor_row(), 0);
// Test the widget color
assert_eq!(term.color(), Color::Black); // Default
term.set_color(Color::DarkGreen);
assert_eq!(term.color(), Color::DarkGreen);
term.set_color(Color::Black);
assert_eq!(term.color(), Color::Black);
term.append(
"This should be one line of white text on black, embedded into the top of a blue field.\n",
);
assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
term.set_output_translate(OutFlags::OFF);
assert_eq!(term.output_translate(), OutFlags::OFF);
term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nDone!\n");
term.set_text_attrib(Attrib::Normal);
}
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?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
sourcepub fn print_char_utf8(&mut self, c: char)
pub fn print_char_utf8(&mut self, c: char)
Prints single UTF-8 char c
at current cursor position, and advances the cursor if the character
is printable. Handles ASCII and control codes (CR, LF, etc).
The character is displayed at the current cursor position using the current text color/attributes.
Handles control codes and can be used to construct ANSI/XTERM escape sequences.
c
must be a single char only (whether UTF-8 or ASCII)c
can be an ASCII character, though not as efficent as print_char()- Invalid UTF-8 chars show the error character (¿) depending on show_unknown(bool).
- Does not trigger redraws
Examples found in repository?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
sourcepub fn put_char(&mut self, c: char, row: i32, col: i32)
pub fn put_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?
374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419
fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
term.reset_terminal();
term.append("0123456789 0\n");
term.append("0123456789 1\n");
term.append("0123456789 2\n");
term.append("0123456789 3\n");
term.append("0123456789 4\n");
term.append("0123456789 5\n");
term.append("0123456789 6\n");
term.append("0123456789 7\n");
term.append("0123456789 8\n");
term.append("0123456789 9\n");
term.append("------------\n");
term.set_text_fg_color(Color::Green);
term.put_char('A', 0, 0);
term.put_char('B', 1, 1);
term.put_char('C', 2, 2);
term.put_char('D', 3, 3);
term.put_char('E', 4, 4);
term.put_char('F', 5, 5);
term.set_text_fg_color(Color::XtermWhite);
assert_eq!(term.cursor_row(), 11);
assert_eq!(term.cursor_col(), 0);
term.set_text_bg_color(Color::DarkBlue);
term.put_char_utf8('b', 8, 1);
term.put_char_utf8('↑', 9, 1);
term.put_char_utf8('c', 8, 2);
term.put_char_utf8('↑', 9, 2);
term.put_char_utf8('d', 8, 3);
term.put_char_utf8('↑', 9, 3);
term.put_char_utf8('e', 8, 4);
term.put_char_utf8('↑', 9, 4);
term.put_char_utf8('f', 8, 5);
term.put_char_utf8('↑', 9, 5);
term.put_char_utf8('g', 8, 6);
term.put_char_utf8('↑', 9, 6);
term.set_text_bg_color(Color::Black);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("Done!\n");
term.set_text_attrib(Attrib::Normal);
}
sourcepub fn put_char_utf8(&mut self, c: char, drow: i32, dcol: i32)
pub fn put_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?
374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419
fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
term.reset_terminal();
term.append("0123456789 0\n");
term.append("0123456789 1\n");
term.append("0123456789 2\n");
term.append("0123456789 3\n");
term.append("0123456789 4\n");
term.append("0123456789 5\n");
term.append("0123456789 6\n");
term.append("0123456789 7\n");
term.append("0123456789 8\n");
term.append("0123456789 9\n");
term.append("------------\n");
term.set_text_fg_color(Color::Green);
term.put_char('A', 0, 0);
term.put_char('B', 1, 1);
term.put_char('C', 2, 2);
term.put_char('D', 3, 3);
term.put_char('E', 4, 4);
term.put_char('F', 5, 5);
term.set_text_fg_color(Color::XtermWhite);
assert_eq!(term.cursor_row(), 11);
assert_eq!(term.cursor_col(), 0);
term.set_text_bg_color(Color::DarkBlue);
term.put_char_utf8('b', 8, 1);
term.put_char_utf8('↑', 9, 1);
term.put_char_utf8('c', 8, 2);
term.put_char_utf8('↑', 9, 2);
term.put_char_utf8('d', 8, 3);
term.put_char_utf8('↑', 9, 3);
term.put_char_utf8('e', 8, 4);
term.put_char_utf8('↑', 9, 4);
term.put_char_utf8('f', 8, 5);
term.put_char_utf8('↑', 9, 5);
term.put_char_utf8('g', 8, 6);
term.put_char_utf8('↑', 9, 6);
term.set_text_bg_color(Color::Black);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("Done!\n");
term.set_text_attrib(Attrib::Normal);
}
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?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
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?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
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?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
sourcepub fn redraw_style(&self) -> RedrawStyle
pub fn redraw_style(&self) -> RedrawStyle
Get the redraw style.
Examples found in repository?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
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?
374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442
fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
term.reset_terminal();
term.append("0123456789 0\n");
term.append("0123456789 1\n");
term.append("0123456789 2\n");
term.append("0123456789 3\n");
term.append("0123456789 4\n");
term.append("0123456789 5\n");
term.append("0123456789 6\n");
term.append("0123456789 7\n");
term.append("0123456789 8\n");
term.append("0123456789 9\n");
term.append("------------\n");
term.set_text_fg_color(Color::Green);
term.put_char('A', 0, 0);
term.put_char('B', 1, 1);
term.put_char('C', 2, 2);
term.put_char('D', 3, 3);
term.put_char('E', 4, 4);
term.put_char('F', 5, 5);
term.set_text_fg_color(Color::XtermWhite);
assert_eq!(term.cursor_row(), 11);
assert_eq!(term.cursor_col(), 0);
term.set_text_bg_color(Color::DarkBlue);
term.put_char_utf8('b', 8, 1);
term.put_char_utf8('↑', 9, 1);
term.put_char_utf8('c', 8, 2);
term.put_char_utf8('↑', 9, 2);
term.put_char_utf8('d', 8, 3);
term.put_char_utf8('↑', 9, 3);
term.put_char_utf8('e', 8, 4);
term.put_char_utf8('↑', 9, 4);
term.put_char_utf8('f', 8, 5);
term.put_char_utf8('↑', 9, 5);
term.put_char_utf8('g', 8, 6);
term.put_char_utf8('↑', 9, 6);
term.set_text_bg_color(Color::Black);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("Done!\n");
term.set_text_attrib(Attrib::Normal);
}
//--------------------------------------------------------------------------------------
/// More tests that run when the menu bar button Test2 is clicked
fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
term.reset_terminal();
for i in 0..50 {
term.append(&format!("{i}\n"));
}
assert_eq!(term.cursor_row(), 30);
assert_eq!(term.display_rows(), 31);
assert_eq!(term.history_rows(), 100);
assert_eq!(term.history_lines(), 100);
assert_eq!(term.history_use(), 20);
term.clear_history();
assert_eq!(term.history_use(), 0);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nDone!\n");
term.set_text_attrib(Attrib::Normal);
}
sourcepub fn scrollbar_actual_size(&self) -> i32
pub fn scrollbar_actual_size(&self) -> i32
Returns the scrollbar’s actual size; actual width for vertical scrollbars, actual height for horizontal scrollbars.
Examples found in repository?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
sourcepub fn scrollbar_size(&self) -> i32
pub fn scrollbar_size(&self) -> i32
Get the current size of the scrollbar’s trough, in pixels. If this value is zero (default), this widget is using fltk’s master scrollbar_size() value
Examples found in repository?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
sourcepub fn set_scrollbar_size(&mut self, val: i32)
pub fn set_scrollbar_size(&mut self, val: i32)
Set the width of the 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?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
sourcepub fn selection_bg_color(&self) -> Color
pub fn selection_bg_color(&self) -> Color
Get mouse selection background color.
Examples found in repository?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
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?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
sourcepub fn selection_fg_color(&self) -> Color
pub fn selection_fg_color(&self) -> Color
Get mouse selection foreground color.
Examples found in repository?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
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?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
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?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
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?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
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?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
//--------------------------------------------------------------------------------------
/// More tests that run when the menu bar Test1 is clicked
fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
term.reset_terminal();
term.append("0123456789 0\n");
term.append("0123456789 1\n");
term.append("0123456789 2\n");
term.append("0123456789 3\n");
term.append("0123456789 4\n");
term.append("0123456789 5\n");
term.append("0123456789 6\n");
term.append("0123456789 7\n");
term.append("0123456789 8\n");
term.append("0123456789 9\n");
term.append("------------\n");
term.set_text_fg_color(Color::Green);
term.put_char('A', 0, 0);
term.put_char('B', 1, 1);
term.put_char('C', 2, 2);
term.put_char('D', 3, 3);
term.put_char('E', 4, 4);
term.put_char('F', 5, 5);
term.set_text_fg_color(Color::XtermWhite);
assert_eq!(term.cursor_row(), 11);
assert_eq!(term.cursor_col(), 0);
term.set_text_bg_color(Color::DarkBlue);
term.put_char_utf8('b', 8, 1);
term.put_char_utf8('↑', 9, 1);
term.put_char_utf8('c', 8, 2);
term.put_char_utf8('↑', 9, 2);
term.put_char_utf8('d', 8, 3);
term.put_char_utf8('↑', 9, 3);
term.put_char_utf8('e', 8, 4);
term.put_char_utf8('↑', 9, 4);
term.put_char_utf8('f', 8, 5);
term.put_char_utf8('↑', 9, 5);
term.put_char_utf8('g', 8, 6);
term.put_char_utf8('↑', 9, 6);
term.set_text_bg_color(Color::Black);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("Done!\n");
term.set_text_attrib(Attrib::Normal);
}
//--------------------------------------------------------------------------------------
/// More tests that run when the menu bar button Test2 is clicked
fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
term.reset_terminal();
for i in 0..50 {
term.append(&format!("{i}\n"));
}
assert_eq!(term.cursor_row(), 30);
assert_eq!(term.display_rows(), 31);
assert_eq!(term.history_rows(), 100);
assert_eq!(term.history_lines(), 100);
assert_eq!(term.history_use(), 20);
term.clear_history();
assert_eq!(term.history_use(), 0);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nDone!\n");
term.set_text_attrib(Attrib::Normal);
}
//--------------------------------------------------------------------------------------
/// Another set of tests that run when Test3 is clicked
fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let hist = term.history_use();
assert_ne!(hist, 0);
term.clear();
assert_eq!(term.cursor_row(), 0);
assert_eq!(term.history_use(), hist + term.display_rows()); // A screenful of lines added to history
term.append("Test\ntext\na\nb\nc\nd");
assert_eq!(term.cursor_row(), 5);
let hist = term.history_use();
term.clear_screen_home(false);
assert_eq!(term.cursor_row(), 0);
assert_eq!(term.history_use(), hist); // History not changed
term.append("Test\ntext\na\nb\nc\nd\ne");
assert_eq!(term.cursor_row(), 6);
term.clear_screen_home(true);
assert_eq!(term.cursor_row(), 0);
let hist = term.history_use();
term.append("Test\ntext\na\nb\nc\n");
assert_eq!(term.cursor_row(), 5);
term.clear_to_color(Color::DarkBlue);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
assert!(term.history_use() > hist); // Some lines added to history
assert_eq!(term.cursor_row(), 0);
// Test cursor_home()
term.append("Test\n\n\n\n\n\n\n\n\n\n");
assert_eq!(term.cursor_row(), 10);
term.cursor_home();
assert_eq!(term.cursor_row(), 0);
// Test the widget color
assert_eq!(term.color(), Color::Black); // Default
term.set_color(Color::DarkGreen);
assert_eq!(term.color(), Color::DarkGreen);
term.set_color(Color::Black);
assert_eq!(term.color(), Color::Black);
term.append(
"This should be one line of white text on black, embedded into the top of a blue field.\n",
);
assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
term.set_output_translate(OutFlags::OFF);
assert_eq!(term.output_translate(), OutFlags::OFF);
term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nDone!\n");
term.set_text_attrib(Attrib::Normal);
}
//--------------------------------------------------------------------------------------
/// Another set of tests for the ring-buffer access methods
fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
// Test the Utf8Char primitive
let uc = Utf8Char::new(b'Q');
let uc1 = uc.text_utf8();
assert_eq!(&uc1, &[b'Q']);
assert_eq!(&uc.attrib(), &Attrib::Normal);
assert_eq!(
&uc.charflags(),
&(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
);
assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
let ring_rows = term.ring_rows();
// println!();
// dbg!(term.disp_srow(), term.disp_erow(), term.disp_rows(), term.ring_cols(), term.ring_srow(), term.ring_erow() );
// dbg!(term.hist_srow(), term.hist_erow(), term.hist_rows(), ring_rows );
// dbg!(term.offset(), term.hist_use_srow(), term.hist_use() );
term.take_focus().unwrap();
term.clear_history();
assert_eq!(term.history_use(), 0);
// Subtract row numbers, modulo `rows`
fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
match a - b {
n if n < 0 => n + rows,
n => n
}
}
// disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert!(term.disp_srow() >= 0);
assert!(term.disp_erow() >= 0);
assert!(term.hist_srow() >= 0);
assert!(term.hist_erow() >= 0);
assert!(term.offset() >= 0);
assert!(term.disp_srow() <= ring_rows);
assert!(term.disp_erow() <= ring_rows);
assert!(term.hist_srow() <= ring_rows);
assert!(term.hist_erow() <= ring_rows);
assert!(term.offset() <= ring_rows);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
assert_eq!(term.ring_erow(), term.ring_rows() - 1);
assert_eq!(term.ring_srow(), 0);
// Check the different cols methods, which should all return the same answer
assert!(term.disp_cols() > 10);
assert_eq!(term.disp_cols(), term.ring_cols());
assert_eq!(term.disp_cols(), term.hist_cols());
// Redundant protected vs public methods:
assert_eq!(term.disp_cols(), term.display_columns());
assert_eq!(term.disp_rows(), term.display_rows());
/// Local function to read back all rows from the display into a long string.
/// Does not include scrollback history.
/// Trims trailing blanks on each line
fn read_disp(term: &Terminal) -> String {
let rows = term.display_rows();
let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
for row in 0..rows {
let r = term.u8c_disp_row(row).trim();
// Iterate through a row, accumulating [u8]
for c in r.iter() {
// Note: Sometimes utf-8 length is > 1
text.extend_from_slice(c.text_utf8());
}
text.extend_from_slice(b"\n");
}
// Return the result as a string
std::str::from_utf8(&text).unwrap().to_string()
}
term.clear();
term.append("Top line ↑ (up-arrow)");
term.set_text_attrib(Attrib::Underline);
term.append(" ");
term.set_text_attrib(Attrib::Normal);
term.append(" \n");
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
// The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
assert_eq!(text_out, "Top line ↑ (up-arrow) \n");
let r = term.u8c_disp_row(0);
assert_eq!(r.col(0).text_utf8(), b"T");
assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
let r = term.u8c_disp_row(1);
assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
// Clear the screen again, then append test text, then read it back and compare
let test_text =
"The wind was a torrent of darkness among the gusty trees.
The moon was a ghostly galleon tossed upon cloudy seas.
The road was a ribbon of moonlight over the purple moor,
And the highwayman came riding—
Riding—riding—
The highwayman came riding, up to the old inn-door.";
term.clear_history();
term.clear();
let bg_save = term.text_bg_color();
let fg_save = term.text_fg_color();
term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
term.append(test_text);
term.set_text_bg_color(bg_save);
term.set_text_fg_color(fg_save);
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
assert_eq!(test_text, text_out);
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
term.append(&format!(
"\n\nScreen has {} rows of {} columns.\n",
term.disp_rows(),
term.disp_cols()
));
term.set_text_attrib(Attrib::Italic);
term.append("Done!");
term.set_text_attrib(Attrib::Normal);
}
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?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
//--------------------------------------------------------------------------------------
/// More tests that run when the menu bar Test1 is clicked
fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
term.reset_terminal();
term.append("0123456789 0\n");
term.append("0123456789 1\n");
term.append("0123456789 2\n");
term.append("0123456789 3\n");
term.append("0123456789 4\n");
term.append("0123456789 5\n");
term.append("0123456789 6\n");
term.append("0123456789 7\n");
term.append("0123456789 8\n");
term.append("0123456789 9\n");
term.append("------------\n");
term.set_text_fg_color(Color::Green);
term.put_char('A', 0, 0);
term.put_char('B', 1, 1);
term.put_char('C', 2, 2);
term.put_char('D', 3, 3);
term.put_char('E', 4, 4);
term.put_char('F', 5, 5);
term.set_text_fg_color(Color::XtermWhite);
assert_eq!(term.cursor_row(), 11);
assert_eq!(term.cursor_col(), 0);
term.set_text_bg_color(Color::DarkBlue);
term.put_char_utf8('b', 8, 1);
term.put_char_utf8('↑', 9, 1);
term.put_char_utf8('c', 8, 2);
term.put_char_utf8('↑', 9, 2);
term.put_char_utf8('d', 8, 3);
term.put_char_utf8('↑', 9, 3);
term.put_char_utf8('e', 8, 4);
term.put_char_utf8('↑', 9, 4);
term.put_char_utf8('f', 8, 5);
term.put_char_utf8('↑', 9, 5);
term.put_char_utf8('g', 8, 6);
term.put_char_utf8('↑', 9, 6);
term.set_text_bg_color(Color::Black);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("Done!\n");
term.set_text_attrib(Attrib::Normal);
}
//--------------------------------------------------------------------------------------
/// More tests that run when the menu bar button Test2 is clicked
fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
term.reset_terminal();
for i in 0..50 {
term.append(&format!("{i}\n"));
}
assert_eq!(term.cursor_row(), 30);
assert_eq!(term.display_rows(), 31);
assert_eq!(term.history_rows(), 100);
assert_eq!(term.history_lines(), 100);
assert_eq!(term.history_use(), 20);
term.clear_history();
assert_eq!(term.history_use(), 0);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nDone!\n");
term.set_text_attrib(Attrib::Normal);
}
//--------------------------------------------------------------------------------------
/// Another set of tests that run when Test3 is clicked
fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let hist = term.history_use();
assert_ne!(hist, 0);
term.clear();
assert_eq!(term.cursor_row(), 0);
assert_eq!(term.history_use(), hist + term.display_rows()); // A screenful of lines added to history
term.append("Test\ntext\na\nb\nc\nd");
assert_eq!(term.cursor_row(), 5);
let hist = term.history_use();
term.clear_screen_home(false);
assert_eq!(term.cursor_row(), 0);
assert_eq!(term.history_use(), hist); // History not changed
term.append("Test\ntext\na\nb\nc\nd\ne");
assert_eq!(term.cursor_row(), 6);
term.clear_screen_home(true);
assert_eq!(term.cursor_row(), 0);
let hist = term.history_use();
term.append("Test\ntext\na\nb\nc\n");
assert_eq!(term.cursor_row(), 5);
term.clear_to_color(Color::DarkBlue);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
assert!(term.history_use() > hist); // Some lines added to history
assert_eq!(term.cursor_row(), 0);
// Test cursor_home()
term.append("Test\n\n\n\n\n\n\n\n\n\n");
assert_eq!(term.cursor_row(), 10);
term.cursor_home();
assert_eq!(term.cursor_row(), 0);
// Test the widget color
assert_eq!(term.color(), Color::Black); // Default
term.set_color(Color::DarkGreen);
assert_eq!(term.color(), Color::DarkGreen);
term.set_color(Color::Black);
assert_eq!(term.color(), Color::Black);
term.append(
"This should be one line of white text on black, embedded into the top of a blue field.\n",
);
assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
term.set_output_translate(OutFlags::OFF);
assert_eq!(term.output_translate(), OutFlags::OFF);
term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nDone!\n");
term.set_text_attrib(Attrib::Normal);
}
//--------------------------------------------------------------------------------------
/// Another set of tests for the ring-buffer access methods
fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
// Test the Utf8Char primitive
let uc = Utf8Char::new(b'Q');
let uc1 = uc.text_utf8();
assert_eq!(&uc1, &[b'Q']);
assert_eq!(&uc.attrib(), &Attrib::Normal);
assert_eq!(
&uc.charflags(),
&(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
);
assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
let ring_rows = term.ring_rows();
// println!();
// dbg!(term.disp_srow(), term.disp_erow(), term.disp_rows(), term.ring_cols(), term.ring_srow(), term.ring_erow() );
// dbg!(term.hist_srow(), term.hist_erow(), term.hist_rows(), ring_rows );
// dbg!(term.offset(), term.hist_use_srow(), term.hist_use() );
term.take_focus().unwrap();
term.clear_history();
assert_eq!(term.history_use(), 0);
// Subtract row numbers, modulo `rows`
fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
match a - b {
n if n < 0 => n + rows,
n => n
}
}
// disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert!(term.disp_srow() >= 0);
assert!(term.disp_erow() >= 0);
assert!(term.hist_srow() >= 0);
assert!(term.hist_erow() >= 0);
assert!(term.offset() >= 0);
assert!(term.disp_srow() <= ring_rows);
assert!(term.disp_erow() <= ring_rows);
assert!(term.hist_srow() <= ring_rows);
assert!(term.hist_erow() <= ring_rows);
assert!(term.offset() <= ring_rows);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
assert_eq!(term.ring_erow(), term.ring_rows() - 1);
assert_eq!(term.ring_srow(), 0);
// Check the different cols methods, which should all return the same answer
assert!(term.disp_cols() > 10);
assert_eq!(term.disp_cols(), term.ring_cols());
assert_eq!(term.disp_cols(), term.hist_cols());
// Redundant protected vs public methods:
assert_eq!(term.disp_cols(), term.display_columns());
assert_eq!(term.disp_rows(), term.display_rows());
/// Local function to read back all rows from the display into a long string.
/// Does not include scrollback history.
/// Trims trailing blanks on each line
fn read_disp(term: &Terminal) -> String {
let rows = term.display_rows();
let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
for row in 0..rows {
let r = term.u8c_disp_row(row).trim();
// Iterate through a row, accumulating [u8]
for c in r.iter() {
// Note: Sometimes utf-8 length is > 1
text.extend_from_slice(c.text_utf8());
}
text.extend_from_slice(b"\n");
}
// Return the result as a string
std::str::from_utf8(&text).unwrap().to_string()
}
term.clear();
term.append("Top line ↑ (up-arrow)");
term.set_text_attrib(Attrib::Underline);
term.append(" ");
term.set_text_attrib(Attrib::Normal);
term.append(" \n");
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
// The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
assert_eq!(text_out, "Top line ↑ (up-arrow) \n");
let r = term.u8c_disp_row(0);
assert_eq!(r.col(0).text_utf8(), b"T");
assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
let r = term.u8c_disp_row(1);
assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
// Clear the screen again, then append test text, then read it back and compare
let test_text =
"The wind was a torrent of darkness among the gusty trees.
The moon was a ghostly galleon tossed upon cloudy seas.
The road was a ribbon of moonlight over the purple moor,
And the highwayman came riding—
Riding—riding—
The highwayman came riding, up to the old inn-door.";
term.clear_history();
term.clear();
let bg_save = term.text_bg_color();
let fg_save = term.text_fg_color();
term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
term.append(test_text);
term.set_text_bg_color(bg_save);
term.set_text_fg_color(fg_save);
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
assert_eq!(test_text, text_out);
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
term.append(&format!(
"\n\nScreen has {} rows of {} columns.\n",
term.disp_rows(),
term.disp_cols()
));
term.set_text_attrib(Attrib::Italic);
term.append("Done!");
term.set_text_attrib(Attrib::Normal);
}
sourcepub fn text_bg_color(&self) -> Color
pub fn text_bg_color(&self) -> Color
Get the text background color.
Examples found in repository?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
//--------------------------------------------------------------------------------------
/// More tests that run when the menu bar Test1 is clicked
fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
term.reset_terminal();
term.append("0123456789 0\n");
term.append("0123456789 1\n");
term.append("0123456789 2\n");
term.append("0123456789 3\n");
term.append("0123456789 4\n");
term.append("0123456789 5\n");
term.append("0123456789 6\n");
term.append("0123456789 7\n");
term.append("0123456789 8\n");
term.append("0123456789 9\n");
term.append("------------\n");
term.set_text_fg_color(Color::Green);
term.put_char('A', 0, 0);
term.put_char('B', 1, 1);
term.put_char('C', 2, 2);
term.put_char('D', 3, 3);
term.put_char('E', 4, 4);
term.put_char('F', 5, 5);
term.set_text_fg_color(Color::XtermWhite);
assert_eq!(term.cursor_row(), 11);
assert_eq!(term.cursor_col(), 0);
term.set_text_bg_color(Color::DarkBlue);
term.put_char_utf8('b', 8, 1);
term.put_char_utf8('↑', 9, 1);
term.put_char_utf8('c', 8, 2);
term.put_char_utf8('↑', 9, 2);
term.put_char_utf8('d', 8, 3);
term.put_char_utf8('↑', 9, 3);
term.put_char_utf8('e', 8, 4);
term.put_char_utf8('↑', 9, 4);
term.put_char_utf8('f', 8, 5);
term.put_char_utf8('↑', 9, 5);
term.put_char_utf8('g', 8, 6);
term.put_char_utf8('↑', 9, 6);
term.set_text_bg_color(Color::Black);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("Done!\n");
term.set_text_attrib(Attrib::Normal);
}
//--------------------------------------------------------------------------------------
/// More tests that run when the menu bar button Test2 is clicked
fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
term.reset_terminal();
for i in 0..50 {
term.append(&format!("{i}\n"));
}
assert_eq!(term.cursor_row(), 30);
assert_eq!(term.display_rows(), 31);
assert_eq!(term.history_rows(), 100);
assert_eq!(term.history_lines(), 100);
assert_eq!(term.history_use(), 20);
term.clear_history();
assert_eq!(term.history_use(), 0);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nDone!\n");
term.set_text_attrib(Attrib::Normal);
}
//--------------------------------------------------------------------------------------
/// Another set of tests that run when Test3 is clicked
fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let hist = term.history_use();
assert_ne!(hist, 0);
term.clear();
assert_eq!(term.cursor_row(), 0);
assert_eq!(term.history_use(), hist + term.display_rows()); // A screenful of lines added to history
term.append("Test\ntext\na\nb\nc\nd");
assert_eq!(term.cursor_row(), 5);
let hist = term.history_use();
term.clear_screen_home(false);
assert_eq!(term.cursor_row(), 0);
assert_eq!(term.history_use(), hist); // History not changed
term.append("Test\ntext\na\nb\nc\nd\ne");
assert_eq!(term.cursor_row(), 6);
term.clear_screen_home(true);
assert_eq!(term.cursor_row(), 0);
let hist = term.history_use();
term.append("Test\ntext\na\nb\nc\n");
assert_eq!(term.cursor_row(), 5);
term.clear_to_color(Color::DarkBlue);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
assert!(term.history_use() > hist); // Some lines added to history
assert_eq!(term.cursor_row(), 0);
// Test cursor_home()
term.append("Test\n\n\n\n\n\n\n\n\n\n");
assert_eq!(term.cursor_row(), 10);
term.cursor_home();
assert_eq!(term.cursor_row(), 0);
// Test the widget color
assert_eq!(term.color(), Color::Black); // Default
term.set_color(Color::DarkGreen);
assert_eq!(term.color(), Color::DarkGreen);
term.set_color(Color::Black);
assert_eq!(term.color(), Color::Black);
term.append(
"This should be one line of white text on black, embedded into the top of a blue field.\n",
);
assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
term.set_output_translate(OutFlags::OFF);
assert_eq!(term.output_translate(), OutFlags::OFF);
term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nDone!\n");
term.set_text_attrib(Attrib::Normal);
}
//--------------------------------------------------------------------------------------
/// Another set of tests for the ring-buffer access methods
fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
// Test the Utf8Char primitive
let uc = Utf8Char::new(b'Q');
let uc1 = uc.text_utf8();
assert_eq!(&uc1, &[b'Q']);
assert_eq!(&uc.attrib(), &Attrib::Normal);
assert_eq!(
&uc.charflags(),
&(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
);
assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
let ring_rows = term.ring_rows();
// println!();
// dbg!(term.disp_srow(), term.disp_erow(), term.disp_rows(), term.ring_cols(), term.ring_srow(), term.ring_erow() );
// dbg!(term.hist_srow(), term.hist_erow(), term.hist_rows(), ring_rows );
// dbg!(term.offset(), term.hist_use_srow(), term.hist_use() );
term.take_focus().unwrap();
term.clear_history();
assert_eq!(term.history_use(), 0);
// Subtract row numbers, modulo `rows`
fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
match a - b {
n if n < 0 => n + rows,
n => n
}
}
// disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert!(term.disp_srow() >= 0);
assert!(term.disp_erow() >= 0);
assert!(term.hist_srow() >= 0);
assert!(term.hist_erow() >= 0);
assert!(term.offset() >= 0);
assert!(term.disp_srow() <= ring_rows);
assert!(term.disp_erow() <= ring_rows);
assert!(term.hist_srow() <= ring_rows);
assert!(term.hist_erow() <= ring_rows);
assert!(term.offset() <= ring_rows);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
assert_eq!(term.ring_erow(), term.ring_rows() - 1);
assert_eq!(term.ring_srow(), 0);
// Check the different cols methods, which should all return the same answer
assert!(term.disp_cols() > 10);
assert_eq!(term.disp_cols(), term.ring_cols());
assert_eq!(term.disp_cols(), term.hist_cols());
// Redundant protected vs public methods:
assert_eq!(term.disp_cols(), term.display_columns());
assert_eq!(term.disp_rows(), term.display_rows());
/// Local function to read back all rows from the display into a long string.
/// Does not include scrollback history.
/// Trims trailing blanks on each line
fn read_disp(term: &Terminal) -> String {
let rows = term.display_rows();
let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
for row in 0..rows {
let r = term.u8c_disp_row(row).trim();
// Iterate through a row, accumulating [u8]
for c in r.iter() {
// Note: Sometimes utf-8 length is > 1
text.extend_from_slice(c.text_utf8());
}
text.extend_from_slice(b"\n");
}
// Return the result as a string
std::str::from_utf8(&text).unwrap().to_string()
}
term.clear();
term.append("Top line ↑ (up-arrow)");
term.set_text_attrib(Attrib::Underline);
term.append(" ");
term.set_text_attrib(Attrib::Normal);
term.append(" \n");
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
// The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
assert_eq!(text_out, "Top line ↑ (up-arrow) \n");
let r = term.u8c_disp_row(0);
assert_eq!(r.col(0).text_utf8(), b"T");
assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
let r = term.u8c_disp_row(1);
assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
// Clear the screen again, then append test text, then read it back and compare
let test_text =
"The wind was a torrent of darkness among the gusty trees.
The moon was a ghostly galleon tossed upon cloudy seas.
The road was a ribbon of moonlight over the purple moor,
And the highwayman came riding—
Riding—riding—
The highwayman came riding, up to the old inn-door.";
term.clear_history();
term.clear();
let bg_save = term.text_bg_color();
let fg_save = term.text_fg_color();
term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
term.append(test_text);
term.set_text_bg_color(bg_save);
term.set_text_fg_color(fg_save);
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
assert_eq!(test_text, text_out);
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
term.append(&format!(
"\n\nScreen has {} rows of {} columns.\n",
term.disp_rows(),
term.disp_cols()
));
term.set_text_attrib(Attrib::Italic);
term.append("Done!");
term.set_text_attrib(Attrib::Normal);
}
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?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
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?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
//--------------------------------------------------------------------------------------
/// More tests that run when the menu bar Test1 is clicked
fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
term.reset_terminal();
term.append("0123456789 0\n");
term.append("0123456789 1\n");
term.append("0123456789 2\n");
term.append("0123456789 3\n");
term.append("0123456789 4\n");
term.append("0123456789 5\n");
term.append("0123456789 6\n");
term.append("0123456789 7\n");
term.append("0123456789 8\n");
term.append("0123456789 9\n");
term.append("------------\n");
term.set_text_fg_color(Color::Green);
term.put_char('A', 0, 0);
term.put_char('B', 1, 1);
term.put_char('C', 2, 2);
term.put_char('D', 3, 3);
term.put_char('E', 4, 4);
term.put_char('F', 5, 5);
term.set_text_fg_color(Color::XtermWhite);
assert_eq!(term.cursor_row(), 11);
assert_eq!(term.cursor_col(), 0);
term.set_text_bg_color(Color::DarkBlue);
term.put_char_utf8('b', 8, 1);
term.put_char_utf8('↑', 9, 1);
term.put_char_utf8('c', 8, 2);
term.put_char_utf8('↑', 9, 2);
term.put_char_utf8('d', 8, 3);
term.put_char_utf8('↑', 9, 3);
term.put_char_utf8('e', 8, 4);
term.put_char_utf8('↑', 9, 4);
term.put_char_utf8('f', 8, 5);
term.put_char_utf8('↑', 9, 5);
term.put_char_utf8('g', 8, 6);
term.put_char_utf8('↑', 9, 6);
term.set_text_bg_color(Color::Black);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("Done!\n");
term.set_text_attrib(Attrib::Normal);
}
//--------------------------------------------------------------------------------------
/// More tests that run when the menu bar button Test2 is clicked
fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
term.reset_terminal();
for i in 0..50 {
term.append(&format!("{i}\n"));
}
assert_eq!(term.cursor_row(), 30);
assert_eq!(term.display_rows(), 31);
assert_eq!(term.history_rows(), 100);
assert_eq!(term.history_lines(), 100);
assert_eq!(term.history_use(), 20);
term.clear_history();
assert_eq!(term.history_use(), 0);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nDone!\n");
term.set_text_attrib(Attrib::Normal);
}
//--------------------------------------------------------------------------------------
/// Another set of tests that run when Test3 is clicked
fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let hist = term.history_use();
assert_ne!(hist, 0);
term.clear();
assert_eq!(term.cursor_row(), 0);
assert_eq!(term.history_use(), hist + term.display_rows()); // A screenful of lines added to history
term.append("Test\ntext\na\nb\nc\nd");
assert_eq!(term.cursor_row(), 5);
let hist = term.history_use();
term.clear_screen_home(false);
assert_eq!(term.cursor_row(), 0);
assert_eq!(term.history_use(), hist); // History not changed
term.append("Test\ntext\na\nb\nc\nd\ne");
assert_eq!(term.cursor_row(), 6);
term.clear_screen_home(true);
assert_eq!(term.cursor_row(), 0);
let hist = term.history_use();
term.append("Test\ntext\na\nb\nc\n");
assert_eq!(term.cursor_row(), 5);
term.clear_to_color(Color::DarkBlue);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
assert!(term.history_use() > hist); // Some lines added to history
assert_eq!(term.cursor_row(), 0);
// Test cursor_home()
term.append("Test\n\n\n\n\n\n\n\n\n\n");
assert_eq!(term.cursor_row(), 10);
term.cursor_home();
assert_eq!(term.cursor_row(), 0);
// Test the widget color
assert_eq!(term.color(), Color::Black); // Default
term.set_color(Color::DarkGreen);
assert_eq!(term.color(), Color::DarkGreen);
term.set_color(Color::Black);
assert_eq!(term.color(), Color::Black);
term.append(
"This should be one line of white text on black, embedded into the top of a blue field.\n",
);
assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
term.set_output_translate(OutFlags::OFF);
assert_eq!(term.output_translate(), OutFlags::OFF);
term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nDone!\n");
term.set_text_attrib(Attrib::Normal);
}
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
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?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
//--------------------------------------------------------------------------------------
/// More tests that run when the menu bar Test1 is clicked
fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
term.reset_terminal();
term.append("0123456789 0\n");
term.append("0123456789 1\n");
term.append("0123456789 2\n");
term.append("0123456789 3\n");
term.append("0123456789 4\n");
term.append("0123456789 5\n");
term.append("0123456789 6\n");
term.append("0123456789 7\n");
term.append("0123456789 8\n");
term.append("0123456789 9\n");
term.append("------------\n");
term.set_text_fg_color(Color::Green);
term.put_char('A', 0, 0);
term.put_char('B', 1, 1);
term.put_char('C', 2, 2);
term.put_char('D', 3, 3);
term.put_char('E', 4, 4);
term.put_char('F', 5, 5);
term.set_text_fg_color(Color::XtermWhite);
assert_eq!(term.cursor_row(), 11);
assert_eq!(term.cursor_col(), 0);
term.set_text_bg_color(Color::DarkBlue);
term.put_char_utf8('b', 8, 1);
term.put_char_utf8('↑', 9, 1);
term.put_char_utf8('c', 8, 2);
term.put_char_utf8('↑', 9, 2);
term.put_char_utf8('d', 8, 3);
term.put_char_utf8('↑', 9, 3);
term.put_char_utf8('e', 8, 4);
term.put_char_utf8('↑', 9, 4);
term.put_char_utf8('f', 8, 5);
term.put_char_utf8('↑', 9, 5);
term.put_char_utf8('g', 8, 6);
term.put_char_utf8('↑', 9, 6);
term.set_text_bg_color(Color::Black);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("Done!\n");
term.set_text_attrib(Attrib::Normal);
}
//--------------------------------------------------------------------------------------
/// More tests that run when the menu bar button Test2 is clicked
fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
term.reset_terminal();
for i in 0..50 {
term.append(&format!("{i}\n"));
}
assert_eq!(term.cursor_row(), 30);
assert_eq!(term.display_rows(), 31);
assert_eq!(term.history_rows(), 100);
assert_eq!(term.history_lines(), 100);
assert_eq!(term.history_use(), 20);
term.clear_history();
assert_eq!(term.history_use(), 0);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nDone!\n");
term.set_text_attrib(Attrib::Normal);
}
//--------------------------------------------------------------------------------------
/// Another set of tests that run when Test3 is clicked
fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let hist = term.history_use();
assert_ne!(hist, 0);
term.clear();
assert_eq!(term.cursor_row(), 0);
assert_eq!(term.history_use(), hist + term.display_rows()); // A screenful of lines added to history
term.append("Test\ntext\na\nb\nc\nd");
assert_eq!(term.cursor_row(), 5);
let hist = term.history_use();
term.clear_screen_home(false);
assert_eq!(term.cursor_row(), 0);
assert_eq!(term.history_use(), hist); // History not changed
term.append("Test\ntext\na\nb\nc\nd\ne");
assert_eq!(term.cursor_row(), 6);
term.clear_screen_home(true);
assert_eq!(term.cursor_row(), 0);
let hist = term.history_use();
term.append("Test\ntext\na\nb\nc\n");
assert_eq!(term.cursor_row(), 5);
term.clear_to_color(Color::DarkBlue);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
assert!(term.history_use() > hist); // Some lines added to history
assert_eq!(term.cursor_row(), 0);
// Test cursor_home()
term.append("Test\n\n\n\n\n\n\n\n\n\n");
assert_eq!(term.cursor_row(), 10);
term.cursor_home();
assert_eq!(term.cursor_row(), 0);
// Test the widget color
assert_eq!(term.color(), Color::Black); // Default
term.set_color(Color::DarkGreen);
assert_eq!(term.color(), Color::DarkGreen);
term.set_color(Color::Black);
assert_eq!(term.color(), Color::Black);
term.append(
"This should be one line of white text on black, embedded into the top of a blue field.\n",
);
assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
term.set_output_translate(OutFlags::OFF);
assert_eq!(term.output_translate(), OutFlags::OFF);
term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nDone!\n");
term.set_text_attrib(Attrib::Normal);
}
//--------------------------------------------------------------------------------------
/// Another set of tests for the ring-buffer access methods
fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
// Test the Utf8Char primitive
let uc = Utf8Char::new(b'Q');
let uc1 = uc.text_utf8();
assert_eq!(&uc1, &[b'Q']);
assert_eq!(&uc.attrib(), &Attrib::Normal);
assert_eq!(
&uc.charflags(),
&(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
);
assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
let ring_rows = term.ring_rows();
// println!();
// dbg!(term.disp_srow(), term.disp_erow(), term.disp_rows(), term.ring_cols(), term.ring_srow(), term.ring_erow() );
// dbg!(term.hist_srow(), term.hist_erow(), term.hist_rows(), ring_rows );
// dbg!(term.offset(), term.hist_use_srow(), term.hist_use() );
term.take_focus().unwrap();
term.clear_history();
assert_eq!(term.history_use(), 0);
// Subtract row numbers, modulo `rows`
fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
match a - b {
n if n < 0 => n + rows,
n => n
}
}
// disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert!(term.disp_srow() >= 0);
assert!(term.disp_erow() >= 0);
assert!(term.hist_srow() >= 0);
assert!(term.hist_erow() >= 0);
assert!(term.offset() >= 0);
assert!(term.disp_srow() <= ring_rows);
assert!(term.disp_erow() <= ring_rows);
assert!(term.hist_srow() <= ring_rows);
assert!(term.hist_erow() <= ring_rows);
assert!(term.offset() <= ring_rows);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
assert_eq!(term.ring_erow(), term.ring_rows() - 1);
assert_eq!(term.ring_srow(), 0);
// Check the different cols methods, which should all return the same answer
assert!(term.disp_cols() > 10);
assert_eq!(term.disp_cols(), term.ring_cols());
assert_eq!(term.disp_cols(), term.hist_cols());
// Redundant protected vs public methods:
assert_eq!(term.disp_cols(), term.display_columns());
assert_eq!(term.disp_rows(), term.display_rows());
/// Local function to read back all rows from the display into a long string.
/// Does not include scrollback history.
/// Trims trailing blanks on each line
fn read_disp(term: &Terminal) -> String {
let rows = term.display_rows();
let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
for row in 0..rows {
let r = term.u8c_disp_row(row).trim();
// Iterate through a row, accumulating [u8]
for c in r.iter() {
// Note: Sometimes utf-8 length is > 1
text.extend_from_slice(c.text_utf8());
}
text.extend_from_slice(b"\n");
}
// Return the result as a string
std::str::from_utf8(&text).unwrap().to_string()
}
term.clear();
term.append("Top line ↑ (up-arrow)");
term.set_text_attrib(Attrib::Underline);
term.append(" ");
term.set_text_attrib(Attrib::Normal);
term.append(" \n");
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
// The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
assert_eq!(text_out, "Top line ↑ (up-arrow) \n");
let r = term.u8c_disp_row(0);
assert_eq!(r.col(0).text_utf8(), b"T");
assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
let r = term.u8c_disp_row(1);
assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
// Clear the screen again, then append test text, then read it back and compare
let test_text =
"The wind was a torrent of darkness among the gusty trees.
The moon was a ghostly galleon tossed upon cloudy seas.
The road was a ribbon of moonlight over the purple moor,
And the highwayman came riding—
Riding—riding—
The highwayman came riding, up to the old inn-door.";
term.clear_history();
term.clear();
let bg_save = term.text_bg_color();
let fg_save = term.text_fg_color();
term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
term.append(test_text);
term.set_text_bg_color(bg_save);
term.set_text_fg_color(fg_save);
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
assert_eq!(test_text, text_out);
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
term.append(&format!(
"\n\nScreen has {} rows of {} columns.\n",
term.disp_rows(),
term.disp_cols()
));
term.set_text_attrib(Attrib::Italic);
term.append("Done!");
term.set_text_attrib(Attrib::Normal);
}
sourcepub fn text_fg_color(&self) -> Color
pub fn text_fg_color(&self) -> Color
Get the text foreground color.
Examples found in repository?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
//--------------------------------------------------------------------------------------
/// More tests that run when the menu bar Test1 is clicked
fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
term.reset_terminal();
term.append("0123456789 0\n");
term.append("0123456789 1\n");
term.append("0123456789 2\n");
term.append("0123456789 3\n");
term.append("0123456789 4\n");
term.append("0123456789 5\n");
term.append("0123456789 6\n");
term.append("0123456789 7\n");
term.append("0123456789 8\n");
term.append("0123456789 9\n");
term.append("------------\n");
term.set_text_fg_color(Color::Green);
term.put_char('A', 0, 0);
term.put_char('B', 1, 1);
term.put_char('C', 2, 2);
term.put_char('D', 3, 3);
term.put_char('E', 4, 4);
term.put_char('F', 5, 5);
term.set_text_fg_color(Color::XtermWhite);
assert_eq!(term.cursor_row(), 11);
assert_eq!(term.cursor_col(), 0);
term.set_text_bg_color(Color::DarkBlue);
term.put_char_utf8('b', 8, 1);
term.put_char_utf8('↑', 9, 1);
term.put_char_utf8('c', 8, 2);
term.put_char_utf8('↑', 9, 2);
term.put_char_utf8('d', 8, 3);
term.put_char_utf8('↑', 9, 3);
term.put_char_utf8('e', 8, 4);
term.put_char_utf8('↑', 9, 4);
term.put_char_utf8('f', 8, 5);
term.put_char_utf8('↑', 9, 5);
term.put_char_utf8('g', 8, 6);
term.put_char_utf8('↑', 9, 6);
term.set_text_bg_color(Color::Black);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("Done!\n");
term.set_text_attrib(Attrib::Normal);
}
//--------------------------------------------------------------------------------------
/// More tests that run when the menu bar button Test2 is clicked
fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
term.reset_terminal();
for i in 0..50 {
term.append(&format!("{i}\n"));
}
assert_eq!(term.cursor_row(), 30);
assert_eq!(term.display_rows(), 31);
assert_eq!(term.history_rows(), 100);
assert_eq!(term.history_lines(), 100);
assert_eq!(term.history_use(), 20);
term.clear_history();
assert_eq!(term.history_use(), 0);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nDone!\n");
term.set_text_attrib(Attrib::Normal);
}
//--------------------------------------------------------------------------------------
/// Another set of tests that run when Test3 is clicked
fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
term.take_focus().unwrap();
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let hist = term.history_use();
assert_ne!(hist, 0);
term.clear();
assert_eq!(term.cursor_row(), 0);
assert_eq!(term.history_use(), hist + term.display_rows()); // A screenful of lines added to history
term.append("Test\ntext\na\nb\nc\nd");
assert_eq!(term.cursor_row(), 5);
let hist = term.history_use();
term.clear_screen_home(false);
assert_eq!(term.cursor_row(), 0);
assert_eq!(term.history_use(), hist); // History not changed
term.append("Test\ntext\na\nb\nc\nd\ne");
assert_eq!(term.cursor_row(), 6);
term.clear_screen_home(true);
assert_eq!(term.cursor_row(), 0);
let hist = term.history_use();
term.append("Test\ntext\na\nb\nc\n");
assert_eq!(term.cursor_row(), 5);
term.clear_to_color(Color::DarkBlue);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
assert!(term.history_use() > hist); // Some lines added to history
assert_eq!(term.cursor_row(), 0);
// Test cursor_home()
term.append("Test\n\n\n\n\n\n\n\n\n\n");
assert_eq!(term.cursor_row(), 10);
term.cursor_home();
assert_eq!(term.cursor_row(), 0);
// Test the widget color
assert_eq!(term.color(), Color::Black); // Default
term.set_color(Color::DarkGreen);
assert_eq!(term.color(), Color::DarkGreen);
term.set_color(Color::Black);
assert_eq!(term.color(), Color::Black);
term.append(
"This should be one line of white text on black, embedded into the top of a blue field.\n",
);
assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
term.set_output_translate(OutFlags::OFF);
assert_eq!(term.output_translate(), OutFlags::OFF);
term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nDone!\n");
term.set_text_attrib(Attrib::Normal);
}
//--------------------------------------------------------------------------------------
/// Another set of tests for the ring-buffer access methods
fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
// Test the Utf8Char primitive
let uc = Utf8Char::new(b'Q');
let uc1 = uc.text_utf8();
assert_eq!(&uc1, &[b'Q']);
assert_eq!(&uc.attrib(), &Attrib::Normal);
assert_eq!(
&uc.charflags(),
&(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
);
assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
let ring_rows = term.ring_rows();
// println!();
// dbg!(term.disp_srow(), term.disp_erow(), term.disp_rows(), term.ring_cols(), term.ring_srow(), term.ring_erow() );
// dbg!(term.hist_srow(), term.hist_erow(), term.hist_rows(), ring_rows );
// dbg!(term.offset(), term.hist_use_srow(), term.hist_use() );
term.take_focus().unwrap();
term.clear_history();
assert_eq!(term.history_use(), 0);
// Subtract row numbers, modulo `rows`
fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
match a - b {
n if n < 0 => n + rows,
n => n
}
}
// disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert!(term.disp_srow() >= 0);
assert!(term.disp_erow() >= 0);
assert!(term.hist_srow() >= 0);
assert!(term.hist_erow() >= 0);
assert!(term.offset() >= 0);
assert!(term.disp_srow() <= ring_rows);
assert!(term.disp_erow() <= ring_rows);
assert!(term.hist_srow() <= ring_rows);
assert!(term.hist_erow() <= ring_rows);
assert!(term.offset() <= ring_rows);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
assert_eq!(term.ring_erow(), term.ring_rows() - 1);
assert_eq!(term.ring_srow(), 0);
// Check the different cols methods, which should all return the same answer
assert!(term.disp_cols() > 10);
assert_eq!(term.disp_cols(), term.ring_cols());
assert_eq!(term.disp_cols(), term.hist_cols());
// Redundant protected vs public methods:
assert_eq!(term.disp_cols(), term.display_columns());
assert_eq!(term.disp_rows(), term.display_rows());
/// Local function to read back all rows from the display into a long string.
/// Does not include scrollback history.
/// Trims trailing blanks on each line
fn read_disp(term: &Terminal) -> String {
let rows = term.display_rows();
let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
for row in 0..rows {
let r = term.u8c_disp_row(row).trim();
// Iterate through a row, accumulating [u8]
for c in r.iter() {
// Note: Sometimes utf-8 length is > 1
text.extend_from_slice(c.text_utf8());
}
text.extend_from_slice(b"\n");
}
// Return the result as a string
std::str::from_utf8(&text).unwrap().to_string()
}
term.clear();
term.append("Top line ↑ (up-arrow)");
term.set_text_attrib(Attrib::Underline);
term.append(" ");
term.set_text_attrib(Attrib::Normal);
term.append(" \n");
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
// The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
assert_eq!(text_out, "Top line ↑ (up-arrow) \n");
let r = term.u8c_disp_row(0);
assert_eq!(r.col(0).text_utf8(), b"T");
assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
let r = term.u8c_disp_row(1);
assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
// Clear the screen again, then append test text, then read it back and compare
let test_text =
"The wind was a torrent of darkness among the gusty trees.
The moon was a ghostly galleon tossed upon cloudy seas.
The road was a ribbon of moonlight over the purple moor,
And the highwayman came riding—
Riding—riding—
The highwayman came riding, up to the old inn-door.";
term.clear_history();
term.clear();
let bg_save = term.text_bg_color();
let fg_save = term.text_fg_color();
term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
term.append(test_text);
term.set_text_bg_color(bg_save);
term.set_text_fg_color(fg_save);
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
assert_eq!(test_text, text_out);
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
term.append(&format!(
"\n\nScreen has {} rows of {} columns.\n",
term.disp_rows(),
term.disp_cols()
));
term.set_text_attrib(Attrib::Italic);
term.append("Done!");
term.set_text_attrib(Attrib::Normal);
}
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?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
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?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
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
sourcepub fn text_font(&self) -> Font
pub fn text_font(&self) -> Font
Get the text font
Examples found in repository?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
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?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
sourcepub fn text_size(&self) -> i32
pub fn text_size(&self) -> i32
Gets the text size
Examples found in repository?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
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?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
fn main() {
let app = fltk::app::App::default();
// Set panic handler for main thread (will become UI thread)
std::panic::set_hook(Box::new({
|e| {
eprintln!("!!!!PANIC!!!!{:#?}", e);
error_box(e.to_string()); // Only works from the UI thread
std::process::exit(2);
}
}));
let mut main_win = Window::new(
2285,
180,
WIN_WIDTH,
WIN_HEIGHT,
"FLTK/Terminal Rust wrapper test",
);
main_win.set_type(WindowType::Double);
main_win.make_resizable(true);
let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
term.set_label("term");
main_win.resizable(&term);
term.set_label_type(LabelType::None);
let idx = menu_bar.add_choice("Test&1");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test1_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
let idx = menu_bar.add_choice("Test&2");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test2_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
let idx = menu_bar.add_choice("Test&3");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test3_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
let idx = menu_bar.add_choice("Test&4");
menu_bar.at(idx).unwrap().set_callback({
let mut term1 = term.clone();
move |c| mb_test4_cb(c, &mut term1)
});
menu_bar
.at(idx)
.unwrap()
.set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
menu_bar.end();
main_win.end();
main_win.show();
// Worker thread that drives the startup tests
let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
let mut term = term.clone();
move || {
println!("Startup tests\n");
term.append("Startup tests\n\n");
// Testing ansi() and set_ansi() methods
assert!(term.ansi(), "Default ANSI mode should be ON at startup");
term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
term.set_ansi(false);
assert!(!term.ansi());
term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
// append() method is already being used/tested. Test the u8, ascii, and utf8 variants
term.append_u8(b"Appending u8 array\n");
term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
// Test show_unknown() as incidental part of testing append methods
term.set_show_unknown(true);
assert!(term.show_unknown());
term.append_ascii(
"Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
);
term.set_show_unknown(false);
assert!(!term.show_unknown());
term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
let r = term.cursor_row();
assert_eq!(term.cursor_col(), 0);
term.append(&format!("Testing cursor row/col {r}"));
assert_eq!(term.cursor_col(), 24);
assert_eq!(term.cursor_row(), r);
// Test cursor color methods
assert_eq!(
term.cursor_bg_color(),
Color::XtermGreen,
"Default cursor bg at startup"
);
assert_eq!(
term.cursor_fg_color(),
Color::from_hex(0xff_ff_f0),
"Default cursor fg at startup"
);
term.set_cursor_bg_color(Color::Red);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
term.set_cursor_fg_color(Color::Blue);
assert_eq!(term.cursor_bg_color(), Color::Red);
assert_eq!(term.cursor_fg_color(), Color::Blue);
term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
// The default display_rows() will derive from the window size
let dr = term.display_rows();
assert!(dr > 20, "Default display_rows at startup");
term.set_display_rows(60);
assert_eq!(term.display_rows(), 60);
term.set_display_rows(dr); // Set back to default
assert_eq!(term.display_rows(), dr);
// The default display_columns() will derive from the window size
let dc = term.display_columns();
assert!(dc > 80, "Default display_rows at startup");
term.set_display_columns(200);
assert_eq!(term.display_columns(), 200);
term.append("\n 1 2 3 4 5 6 7 8 9");
term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
term.set_display_columns(90);
assert_eq!(term.display_columns(), 90);
term.set_display_columns(dc); // Set back to default
assert_eq!(term.display_columns(), dc);
let hl = term.history_lines();
assert_eq!(hl, 100, "Default history_lines at startup");
term.set_history_lines(60);
assert_eq!(term.history_lines(), 60);
term.set_history_lines(hl); // Set back to default
assert_eq!(term.history_lines(), hl);
// Is history_rows() an alias for history_lines()?
assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
term.set_history_rows(50);
assert_eq!(term.history_rows(), 50);
term.set_history_lines(100); // Set back to default
assert_eq!(term.history_lines(), 100);
let hu = term.history_use();
term.append(&format!(
"history_use = {hu} (it's not clear what this means)\n"
));
// assert_eq!(term.history_use(), hu+1);
term.append(&format!(
"margins = b:{} l:{} r:{} t{}\n",
term.margin_bottom(),
term.margin_left(),
term.margin_right(),
term.margin_top()
));
assert_eq!(term.margin_bottom(), 3);
assert_eq!(term.margin_left(), 3);
assert_eq!(term.margin_right(), 3);
assert_eq!(term.margin_top(), 3);
term.set_margin_bottom(5);
term.set_margin_left(10);
term.set_margin_right(15);
term.set_margin_top(20);
assert_eq!(term.margin_bottom(), 5);
assert_eq!(term.margin_left(), 10);
assert_eq!(term.margin_right(), 15);
assert_eq!(term.margin_top(), 20);
term.append("Single character: '");
term.print_char('X');
term.append("', single UTF-8 character: '");
term.print_char_utf8('↑');
term.append("'\n");
let rr = term.redraw_rate();
assert_eq!(rr, 0.1, "Default redraw rate at startup");
term.append(&format!("Redraw rate {rr}\n"));
term.set_redraw_rate(1.0);
assert_eq!(term.redraw_rate(), 1.0);
term.set_redraw_rate(rr);
assert_eq!(term.redraw_rate(), rr);
let rs = term.redraw_style();
term.append(&format!("Redraw style {rs:?}\n"));
assert_eq!(
rs,
RedrawStyle::RateLimited,
"Default redraw style at startup"
);
term.set_redraw_style(RedrawStyle::NoRedraw);
assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
term.set_redraw_style(rs);
assert_eq!(term.redraw_style(), rs);
// Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
assert_eq!(
RedrawStyle::NoRedraw.bits(),
0x0000,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::RateLimited.bits(),
0x0001,
"RedrawStyle enum values have been reassigned"
);
assert_eq!(
RedrawStyle::PerWrite.bits(),
0x0002,
"RedrawStyle enum values have been reassigned"
);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
assert_eq!(term.scrollbar_actual_size(), 16);
term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
assert_eq!(
term.scrollbar_size(),
0,
"Default scrollbar size at startup"
);
term.set_scrollbar_size(40);
assert_eq!(term.scrollbar_size(), 40);
assert_eq!(term.scrollbar_actual_size(), 40);
term.append(&format!(
"Scrollbar actual size {}\n",
term.scrollbar_actual_size()
));
term.set_scrollbar_size(0); // Restore default
assert_eq!(term.scrollbar_size(), 0);
assert_eq!(term.scrollbar_actual_size(), 16);
let sfc = term.selection_fg_color();
let sbc = term.selection_bg_color();
assert_eq!(sfc, Color::Black);
assert_eq!(sbc, Color::White);
term.append(&format!("Selection colors: {sfc} {sbc}\n"));
term.set_selection_fg_color(Color::Green);
term.set_selection_bg_color(Color::DarkBlue);
assert_eq!(term.selection_fg_color(), Color::Green);
assert_eq!(term.selection_bg_color(), Color::DarkBlue);
term.set_selection_fg_color(sfc);
term.set_selection_bg_color(sbc);
assert_eq!(term.selection_fg_color(), Color::Black);
assert_eq!(term.selection_bg_color(), Color::White);
let tfcd = term.text_fg_color_default();
let tbcd = term.text_bg_color_default();
assert_eq!(tfcd, Color::XtermWhite);
assert_eq!(tbcd, Color::TransparentBg);
term.append(&format!("Default text colors: {sfc} {sbc}\n"));
term.set_text_fg_color_default(Color::Green);
term.set_text_bg_color_default(Color::DarkBlue);
assert_eq!(term.text_fg_color_default(), Color::Green);
assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
term.set_text_fg_color_default(tfcd);
term.set_text_bg_color_default(tbcd);
assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
let tfc = term.text_fg_color();
let tbc = term.text_bg_color();
assert_eq!(tfc, Color::XtermWhite);
assert_eq!(tbc, Color::TransparentBg);
term.append(&format!("Text colors: {sfc} {sbc}\n"));
term.set_text_fg_color(Color::Green);
term.set_text_bg_color(Color::DarkBlue);
assert_eq!(term.text_fg_color(), Color::Green);
assert_eq!(term.text_bg_color(), Color::DarkBlue);
term.set_text_fg_color(tfc);
term.set_text_bg_color(tbc);
assert_eq!(term.text_fg_color(), Color::XtermWhite);
assert_eq!(term.text_bg_color(), Color::TransparentBg);
let tf = term.text_font();
term.append(&format!("Text font: {tf:?}\n"));
assert_eq!(tf, Font::Courier);
term.set_text_font(Font::Screen);
assert_eq!(term.text_font(), Font::Screen);
term.set_text_font(tf);
assert_eq!(term.text_font(), Font::Courier);
let ts = term.text_size();
term.append(&format!("Text size: {ts}\n"));
assert_eq!(ts, 14);
term.set_text_size(30);
assert_eq!(term.text_size(), 30);
term.set_text_size(ts);
assert_eq!(term.text_size(), ts);
// Keyboard handler
term.handle({
let mut term = term.clone();
move |_kc, e| {
match e {
fltk::enums::Event::KeyDown
if fltk::app::event_key() == fltk::enums::Key::Escape =>
{
// let FLTK handle ESC
false
}
fltk::enums::Event::KeyDown
if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
{
// We handle normal printable keystroke
let k = fltk::app::event_text();
term.take_focus().unwrap();
term.append(&k);
true
}
// fltk docs say that keyboard handler should always claim Focus and Unfocus events
fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => true,
_ => false, // Let FLTK handle everything else
}
}
});
term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
term.append("\nStartup tests complete. Keyboard is live.\n");
term.set_text_attrib(Attrib::Normal);
term.redraw();
}
});
app.run().unwrap();
}
sourcepub fn selection_text(&self) -> String
pub fn selection_text(&self) -> String
Gets the selection text
sourcepub fn disp_erow(&self) -> i32
pub fn disp_erow(&self) -> i32
Return the ending row# in the display area.
Examples found in repository?
506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
// Test the Utf8Char primitive
let uc = Utf8Char::new(b'Q');
let uc1 = uc.text_utf8();
assert_eq!(&uc1, &[b'Q']);
assert_eq!(&uc.attrib(), &Attrib::Normal);
assert_eq!(
&uc.charflags(),
&(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
);
assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
let ring_rows = term.ring_rows();
// println!();
// dbg!(term.disp_srow(), term.disp_erow(), term.disp_rows(), term.ring_cols(), term.ring_srow(), term.ring_erow() );
// dbg!(term.hist_srow(), term.hist_erow(), term.hist_rows(), ring_rows );
// dbg!(term.offset(), term.hist_use_srow(), term.hist_use() );
term.take_focus().unwrap();
term.clear_history();
assert_eq!(term.history_use(), 0);
// Subtract row numbers, modulo `rows`
fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
match a - b {
n if n < 0 => n + rows,
n => n
}
}
// disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert!(term.disp_srow() >= 0);
assert!(term.disp_erow() >= 0);
assert!(term.hist_srow() >= 0);
assert!(term.hist_erow() >= 0);
assert!(term.offset() >= 0);
assert!(term.disp_srow() <= ring_rows);
assert!(term.disp_erow() <= ring_rows);
assert!(term.hist_srow() <= ring_rows);
assert!(term.hist_erow() <= ring_rows);
assert!(term.offset() <= ring_rows);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
assert_eq!(term.ring_erow(), term.ring_rows() - 1);
assert_eq!(term.ring_srow(), 0);
// Check the different cols methods, which should all return the same answer
assert!(term.disp_cols() > 10);
assert_eq!(term.disp_cols(), term.ring_cols());
assert_eq!(term.disp_cols(), term.hist_cols());
// Redundant protected vs public methods:
assert_eq!(term.disp_cols(), term.display_columns());
assert_eq!(term.disp_rows(), term.display_rows());
/// Local function to read back all rows from the display into a long string.
/// Does not include scrollback history.
/// Trims trailing blanks on each line
fn read_disp(term: &Terminal) -> String {
let rows = term.display_rows();
let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
for row in 0..rows {
let r = term.u8c_disp_row(row).trim();
// Iterate through a row, accumulating [u8]
for c in r.iter() {
// Note: Sometimes utf-8 length is > 1
text.extend_from_slice(c.text_utf8());
}
text.extend_from_slice(b"\n");
}
// Return the result as a string
std::str::from_utf8(&text).unwrap().to_string()
}
term.clear();
term.append("Top line ↑ (up-arrow)");
term.set_text_attrib(Attrib::Underline);
term.append(" ");
term.set_text_attrib(Attrib::Normal);
term.append(" \n");
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
// The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
assert_eq!(text_out, "Top line ↑ (up-arrow) \n");
let r = term.u8c_disp_row(0);
assert_eq!(r.col(0).text_utf8(), b"T");
assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
let r = term.u8c_disp_row(1);
assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
// Clear the screen again, then append test text, then read it back and compare
let test_text =
"The wind was a torrent of darkness among the gusty trees.
The moon was a ghostly galleon tossed upon cloudy seas.
The road was a ribbon of moonlight over the purple moor,
And the highwayman came riding—
Riding—riding—
The highwayman came riding, up to the old inn-door.";
term.clear_history();
term.clear();
let bg_save = term.text_bg_color();
let fg_save = term.text_fg_color();
term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
term.append(test_text);
term.set_text_bg_color(bg_save);
term.set_text_fg_color(fg_save);
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
assert_eq!(test_text, text_out);
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
term.append(&format!(
"\n\nScreen has {} rows of {} columns.\n",
term.disp_rows(),
term.disp_cols()
));
term.set_text_attrib(Attrib::Italic);
term.append("Done!");
term.set_text_attrib(Attrib::Normal);
}
sourcepub fn disp_rows(&self) -> i32
pub fn disp_rows(&self) -> i32
Return the number of rows in the display area.
Examples found in repository?
506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
// Test the Utf8Char primitive
let uc = Utf8Char::new(b'Q');
let uc1 = uc.text_utf8();
assert_eq!(&uc1, &[b'Q']);
assert_eq!(&uc.attrib(), &Attrib::Normal);
assert_eq!(
&uc.charflags(),
&(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
);
assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
let ring_rows = term.ring_rows();
// println!();
// dbg!(term.disp_srow(), term.disp_erow(), term.disp_rows(), term.ring_cols(), term.ring_srow(), term.ring_erow() );
// dbg!(term.hist_srow(), term.hist_erow(), term.hist_rows(), ring_rows );
// dbg!(term.offset(), term.hist_use_srow(), term.hist_use() );
term.take_focus().unwrap();
term.clear_history();
assert_eq!(term.history_use(), 0);
// Subtract row numbers, modulo `rows`
fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
match a - b {
n if n < 0 => n + rows,
n => n
}
}
// disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert!(term.disp_srow() >= 0);
assert!(term.disp_erow() >= 0);
assert!(term.hist_srow() >= 0);
assert!(term.hist_erow() >= 0);
assert!(term.offset() >= 0);
assert!(term.disp_srow() <= ring_rows);
assert!(term.disp_erow() <= ring_rows);
assert!(term.hist_srow() <= ring_rows);
assert!(term.hist_erow() <= ring_rows);
assert!(term.offset() <= ring_rows);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
assert_eq!(term.ring_erow(), term.ring_rows() - 1);
assert_eq!(term.ring_srow(), 0);
// Check the different cols methods, which should all return the same answer
assert!(term.disp_cols() > 10);
assert_eq!(term.disp_cols(), term.ring_cols());
assert_eq!(term.disp_cols(), term.hist_cols());
// Redundant protected vs public methods:
assert_eq!(term.disp_cols(), term.display_columns());
assert_eq!(term.disp_rows(), term.display_rows());
/// Local function to read back all rows from the display into a long string.
/// Does not include scrollback history.
/// Trims trailing blanks on each line
fn read_disp(term: &Terminal) -> String {
let rows = term.display_rows();
let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
for row in 0..rows {
let r = term.u8c_disp_row(row).trim();
// Iterate through a row, accumulating [u8]
for c in r.iter() {
// Note: Sometimes utf-8 length is > 1
text.extend_from_slice(c.text_utf8());
}
text.extend_from_slice(b"\n");
}
// Return the result as a string
std::str::from_utf8(&text).unwrap().to_string()
}
term.clear();
term.append("Top line ↑ (up-arrow)");
term.set_text_attrib(Attrib::Underline);
term.append(" ");
term.set_text_attrib(Attrib::Normal);
term.append(" \n");
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
// The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
assert_eq!(text_out, "Top line ↑ (up-arrow) \n");
let r = term.u8c_disp_row(0);
assert_eq!(r.col(0).text_utf8(), b"T");
assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
let r = term.u8c_disp_row(1);
assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
// Clear the screen again, then append test text, then read it back and compare
let test_text =
"The wind was a torrent of darkness among the gusty trees.
The moon was a ghostly galleon tossed upon cloudy seas.
The road was a ribbon of moonlight over the purple moor,
And the highwayman came riding—
Riding—riding—
The highwayman came riding, up to the old inn-door.";
term.clear_history();
term.clear();
let bg_save = term.text_bg_color();
let fg_save = term.text_fg_color();
term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
term.append(test_text);
term.set_text_bg_color(bg_save);
term.set_text_fg_color(fg_save);
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
assert_eq!(test_text, text_out);
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
term.append(&format!(
"\n\nScreen has {} rows of {} columns.\n",
term.disp_rows(),
term.disp_cols()
));
term.set_text_attrib(Attrib::Italic);
term.append("Done!");
term.set_text_attrib(Attrib::Normal);
}
sourcepub fn disp_cols(&self) -> i32
pub fn disp_cols(&self) -> i32
Return the number of columns in the display area (always the same as ring_cols())
Examples found in repository?
506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
// Test the Utf8Char primitive
let uc = Utf8Char::new(b'Q');
let uc1 = uc.text_utf8();
assert_eq!(&uc1, &[b'Q']);
assert_eq!(&uc.attrib(), &Attrib::Normal);
assert_eq!(
&uc.charflags(),
&(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
);
assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
let ring_rows = term.ring_rows();
// println!();
// dbg!(term.disp_srow(), term.disp_erow(), term.disp_rows(), term.ring_cols(), term.ring_srow(), term.ring_erow() );
// dbg!(term.hist_srow(), term.hist_erow(), term.hist_rows(), ring_rows );
// dbg!(term.offset(), term.hist_use_srow(), term.hist_use() );
term.take_focus().unwrap();
term.clear_history();
assert_eq!(term.history_use(), 0);
// Subtract row numbers, modulo `rows`
fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
match a - b {
n if n < 0 => n + rows,
n => n
}
}
// disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert!(term.disp_srow() >= 0);
assert!(term.disp_erow() >= 0);
assert!(term.hist_srow() >= 0);
assert!(term.hist_erow() >= 0);
assert!(term.offset() >= 0);
assert!(term.disp_srow() <= ring_rows);
assert!(term.disp_erow() <= ring_rows);
assert!(term.hist_srow() <= ring_rows);
assert!(term.hist_erow() <= ring_rows);
assert!(term.offset() <= ring_rows);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
assert_eq!(term.ring_erow(), term.ring_rows() - 1);
assert_eq!(term.ring_srow(), 0);
// Check the different cols methods, which should all return the same answer
assert!(term.disp_cols() > 10);
assert_eq!(term.disp_cols(), term.ring_cols());
assert_eq!(term.disp_cols(), term.hist_cols());
// Redundant protected vs public methods:
assert_eq!(term.disp_cols(), term.display_columns());
assert_eq!(term.disp_rows(), term.display_rows());
/// Local function to read back all rows from the display into a long string.
/// Does not include scrollback history.
/// Trims trailing blanks on each line
fn read_disp(term: &Terminal) -> String {
let rows = term.display_rows();
let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
for row in 0..rows {
let r = term.u8c_disp_row(row).trim();
// Iterate through a row, accumulating [u8]
for c in r.iter() {
// Note: Sometimes utf-8 length is > 1
text.extend_from_slice(c.text_utf8());
}
text.extend_from_slice(b"\n");
}
// Return the result as a string
std::str::from_utf8(&text).unwrap().to_string()
}
term.clear();
term.append("Top line ↑ (up-arrow)");
term.set_text_attrib(Attrib::Underline);
term.append(" ");
term.set_text_attrib(Attrib::Normal);
term.append(" \n");
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
// The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
assert_eq!(text_out, "Top line ↑ (up-arrow) \n");
let r = term.u8c_disp_row(0);
assert_eq!(r.col(0).text_utf8(), b"T");
assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
let r = term.u8c_disp_row(1);
assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
// Clear the screen again, then append test text, then read it back and compare
let test_text =
"The wind was a torrent of darkness among the gusty trees.
The moon was a ghostly galleon tossed upon cloudy seas.
The road was a ribbon of moonlight over the purple moor,
And the highwayman came riding—
Riding—riding—
The highwayman came riding, up to the old inn-door.";
term.clear_history();
term.clear();
let bg_save = term.text_bg_color();
let fg_save = term.text_fg_color();
term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
term.append(test_text);
term.set_text_bg_color(bg_save);
term.set_text_fg_color(fg_save);
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
assert_eq!(test_text, text_out);
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
term.append(&format!(
"\n\nScreen has {} rows of {} columns.\n",
term.disp_rows(),
term.disp_cols()
));
term.set_text_attrib(Attrib::Italic);
term.append("Done!");
term.set_text_attrib(Attrib::Normal);
}
sourcepub fn disp_srow(&self) -> i32
pub fn disp_srow(&self) -> i32
Return the starting row# in the display area.
Examples found in repository?
506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
// Test the Utf8Char primitive
let uc = Utf8Char::new(b'Q');
let uc1 = uc.text_utf8();
assert_eq!(&uc1, &[b'Q']);
assert_eq!(&uc.attrib(), &Attrib::Normal);
assert_eq!(
&uc.charflags(),
&(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
);
assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
let ring_rows = term.ring_rows();
// println!();
// dbg!(term.disp_srow(), term.disp_erow(), term.disp_rows(), term.ring_cols(), term.ring_srow(), term.ring_erow() );
// dbg!(term.hist_srow(), term.hist_erow(), term.hist_rows(), ring_rows );
// dbg!(term.offset(), term.hist_use_srow(), term.hist_use() );
term.take_focus().unwrap();
term.clear_history();
assert_eq!(term.history_use(), 0);
// Subtract row numbers, modulo `rows`
fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
match a - b {
n if n < 0 => n + rows,
n => n
}
}
// disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert!(term.disp_srow() >= 0);
assert!(term.disp_erow() >= 0);
assert!(term.hist_srow() >= 0);
assert!(term.hist_erow() >= 0);
assert!(term.offset() >= 0);
assert!(term.disp_srow() <= ring_rows);
assert!(term.disp_erow() <= ring_rows);
assert!(term.hist_srow() <= ring_rows);
assert!(term.hist_erow() <= ring_rows);
assert!(term.offset() <= ring_rows);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
assert_eq!(term.ring_erow(), term.ring_rows() - 1);
assert_eq!(term.ring_srow(), 0);
// Check the different cols methods, which should all return the same answer
assert!(term.disp_cols() > 10);
assert_eq!(term.disp_cols(), term.ring_cols());
assert_eq!(term.disp_cols(), term.hist_cols());
// Redundant protected vs public methods:
assert_eq!(term.disp_cols(), term.display_columns());
assert_eq!(term.disp_rows(), term.display_rows());
/// Local function to read back all rows from the display into a long string.
/// Does not include scrollback history.
/// Trims trailing blanks on each line
fn read_disp(term: &Terminal) -> String {
let rows = term.display_rows();
let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
for row in 0..rows {
let r = term.u8c_disp_row(row).trim();
// Iterate through a row, accumulating [u8]
for c in r.iter() {
// Note: Sometimes utf-8 length is > 1
text.extend_from_slice(c.text_utf8());
}
text.extend_from_slice(b"\n");
}
// Return the result as a string
std::str::from_utf8(&text).unwrap().to_string()
}
term.clear();
term.append("Top line ↑ (up-arrow)");
term.set_text_attrib(Attrib::Underline);
term.append(" ");
term.set_text_attrib(Attrib::Normal);
term.append(" \n");
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
// The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
assert_eq!(text_out, "Top line ↑ (up-arrow) \n");
let r = term.u8c_disp_row(0);
assert_eq!(r.col(0).text_utf8(), b"T");
assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
let r = term.u8c_disp_row(1);
assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
// Clear the screen again, then append test text, then read it back and compare
let test_text =
"The wind was a torrent of darkness among the gusty trees.
The moon was a ghostly galleon tossed upon cloudy seas.
The road was a ribbon of moonlight over the purple moor,
And the highwayman came riding—
Riding—riding—
The highwayman came riding, up to the old inn-door.";
term.clear_history();
term.clear();
let bg_save = term.text_bg_color();
let fg_save = term.text_fg_color();
term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
term.append(test_text);
term.set_text_bg_color(bg_save);
term.set_text_fg_color(fg_save);
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
assert_eq!(test_text, text_out);
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
term.append(&format!(
"\n\nScreen has {} rows of {} columns.\n",
term.disp_rows(),
term.disp_cols()
));
term.set_text_attrib(Attrib::Italic);
term.append("Done!");
term.set_text_attrib(Attrib::Normal);
}
sourcepub fn hist_cols(&self) -> i32
pub fn hist_cols(&self) -> i32
Return the number of columns in the scrollback history (always the same as ring_cols())
Examples found in repository?
506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
// Test the Utf8Char primitive
let uc = Utf8Char::new(b'Q');
let uc1 = uc.text_utf8();
assert_eq!(&uc1, &[b'Q']);
assert_eq!(&uc.attrib(), &Attrib::Normal);
assert_eq!(
&uc.charflags(),
&(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
);
assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
let ring_rows = term.ring_rows();
// println!();
// dbg!(term.disp_srow(), term.disp_erow(), term.disp_rows(), term.ring_cols(), term.ring_srow(), term.ring_erow() );
// dbg!(term.hist_srow(), term.hist_erow(), term.hist_rows(), ring_rows );
// dbg!(term.offset(), term.hist_use_srow(), term.hist_use() );
term.take_focus().unwrap();
term.clear_history();
assert_eq!(term.history_use(), 0);
// Subtract row numbers, modulo `rows`
fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
match a - b {
n if n < 0 => n + rows,
n => n
}
}
// disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert!(term.disp_srow() >= 0);
assert!(term.disp_erow() >= 0);
assert!(term.hist_srow() >= 0);
assert!(term.hist_erow() >= 0);
assert!(term.offset() >= 0);
assert!(term.disp_srow() <= ring_rows);
assert!(term.disp_erow() <= ring_rows);
assert!(term.hist_srow() <= ring_rows);
assert!(term.hist_erow() <= ring_rows);
assert!(term.offset() <= ring_rows);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
assert_eq!(term.ring_erow(), term.ring_rows() - 1);
assert_eq!(term.ring_srow(), 0);
// Check the different cols methods, which should all return the same answer
assert!(term.disp_cols() > 10);
assert_eq!(term.disp_cols(), term.ring_cols());
assert_eq!(term.disp_cols(), term.hist_cols());
// Redundant protected vs public methods:
assert_eq!(term.disp_cols(), term.display_columns());
assert_eq!(term.disp_rows(), term.display_rows());
/// Local function to read back all rows from the display into a long string.
/// Does not include scrollback history.
/// Trims trailing blanks on each line
fn read_disp(term: &Terminal) -> String {
let rows = term.display_rows();
let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
for row in 0..rows {
let r = term.u8c_disp_row(row).trim();
// Iterate through a row, accumulating [u8]
for c in r.iter() {
// Note: Sometimes utf-8 length is > 1
text.extend_from_slice(c.text_utf8());
}
text.extend_from_slice(b"\n");
}
// Return the result as a string
std::str::from_utf8(&text).unwrap().to_string()
}
term.clear();
term.append("Top line ↑ (up-arrow)");
term.set_text_attrib(Attrib::Underline);
term.append(" ");
term.set_text_attrib(Attrib::Normal);
term.append(" \n");
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
// The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
assert_eq!(text_out, "Top line ↑ (up-arrow) \n");
let r = term.u8c_disp_row(0);
assert_eq!(r.col(0).text_utf8(), b"T");
assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
let r = term.u8c_disp_row(1);
assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
// Clear the screen again, then append test text, then read it back and compare
let test_text =
"The wind was a torrent of darkness among the gusty trees.
The moon was a ghostly galleon tossed upon cloudy seas.
The road was a ribbon of moonlight over the purple moor,
And the highwayman came riding—
Riding—riding—
The highwayman came riding, up to the old inn-door.";
term.clear_history();
term.clear();
let bg_save = term.text_bg_color();
let fg_save = term.text_fg_color();
term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
term.append(test_text);
term.set_text_bg_color(bg_save);
term.set_text_fg_color(fg_save);
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
assert_eq!(test_text, text_out);
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
term.append(&format!(
"\n\nScreen has {} rows of {} columns.\n",
term.disp_rows(),
term.disp_cols()
));
term.set_text_attrib(Attrib::Italic);
term.append("Done!");
term.set_text_attrib(Attrib::Normal);
}
sourcepub fn hist_erow(&self) -> i32
pub fn hist_erow(&self) -> i32
Return the ending row# of the scrollback history.
Examples found in repository?
506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
// Test the Utf8Char primitive
let uc = Utf8Char::new(b'Q');
let uc1 = uc.text_utf8();
assert_eq!(&uc1, &[b'Q']);
assert_eq!(&uc.attrib(), &Attrib::Normal);
assert_eq!(
&uc.charflags(),
&(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
);
assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
let ring_rows = term.ring_rows();
// println!();
// dbg!(term.disp_srow(), term.disp_erow(), term.disp_rows(), term.ring_cols(), term.ring_srow(), term.ring_erow() );
// dbg!(term.hist_srow(), term.hist_erow(), term.hist_rows(), ring_rows );
// dbg!(term.offset(), term.hist_use_srow(), term.hist_use() );
term.take_focus().unwrap();
term.clear_history();
assert_eq!(term.history_use(), 0);
// Subtract row numbers, modulo `rows`
fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
match a - b {
n if n < 0 => n + rows,
n => n
}
}
// disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert!(term.disp_srow() >= 0);
assert!(term.disp_erow() >= 0);
assert!(term.hist_srow() >= 0);
assert!(term.hist_erow() >= 0);
assert!(term.offset() >= 0);
assert!(term.disp_srow() <= ring_rows);
assert!(term.disp_erow() <= ring_rows);
assert!(term.hist_srow() <= ring_rows);
assert!(term.hist_erow() <= ring_rows);
assert!(term.offset() <= ring_rows);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
assert_eq!(term.ring_erow(), term.ring_rows() - 1);
assert_eq!(term.ring_srow(), 0);
// Check the different cols methods, which should all return the same answer
assert!(term.disp_cols() > 10);
assert_eq!(term.disp_cols(), term.ring_cols());
assert_eq!(term.disp_cols(), term.hist_cols());
// Redundant protected vs public methods:
assert_eq!(term.disp_cols(), term.display_columns());
assert_eq!(term.disp_rows(), term.display_rows());
/// Local function to read back all rows from the display into a long string.
/// Does not include scrollback history.
/// Trims trailing blanks on each line
fn read_disp(term: &Terminal) -> String {
let rows = term.display_rows();
let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
for row in 0..rows {
let r = term.u8c_disp_row(row).trim();
// Iterate through a row, accumulating [u8]
for c in r.iter() {
// Note: Sometimes utf-8 length is > 1
text.extend_from_slice(c.text_utf8());
}
text.extend_from_slice(b"\n");
}
// Return the result as a string
std::str::from_utf8(&text).unwrap().to_string()
}
term.clear();
term.append("Top line ↑ (up-arrow)");
term.set_text_attrib(Attrib::Underline);
term.append(" ");
term.set_text_attrib(Attrib::Normal);
term.append(" \n");
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
// The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
assert_eq!(text_out, "Top line ↑ (up-arrow) \n");
let r = term.u8c_disp_row(0);
assert_eq!(r.col(0).text_utf8(), b"T");
assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
let r = term.u8c_disp_row(1);
assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
// Clear the screen again, then append test text, then read it back and compare
let test_text =
"The wind was a torrent of darkness among the gusty trees.
The moon was a ghostly galleon tossed upon cloudy seas.
The road was a ribbon of moonlight over the purple moor,
And the highwayman came riding—
Riding—riding—
The highwayman came riding, up to the old inn-door.";
term.clear_history();
term.clear();
let bg_save = term.text_bg_color();
let fg_save = term.text_fg_color();
term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
term.append(test_text);
term.set_text_bg_color(bg_save);
term.set_text_fg_color(fg_save);
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
assert_eq!(test_text, text_out);
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
term.append(&format!(
"\n\nScreen has {} rows of {} columns.\n",
term.disp_rows(),
term.disp_cols()
));
term.set_text_attrib(Attrib::Italic);
term.append("Done!");
term.set_text_attrib(Attrib::Normal);
}
sourcepub fn hist_rows(&self) -> i32
pub fn hist_rows(&self) -> i32
Return the number of rows in the scrollback history.
Examples found in repository?
506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
// Test the Utf8Char primitive
let uc = Utf8Char::new(b'Q');
let uc1 = uc.text_utf8();
assert_eq!(&uc1, &[b'Q']);
assert_eq!(&uc.attrib(), &Attrib::Normal);
assert_eq!(
&uc.charflags(),
&(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
);
assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
let ring_rows = term.ring_rows();
// println!();
// dbg!(term.disp_srow(), term.disp_erow(), term.disp_rows(), term.ring_cols(), term.ring_srow(), term.ring_erow() );
// dbg!(term.hist_srow(), term.hist_erow(), term.hist_rows(), ring_rows );
// dbg!(term.offset(), term.hist_use_srow(), term.hist_use() );
term.take_focus().unwrap();
term.clear_history();
assert_eq!(term.history_use(), 0);
// Subtract row numbers, modulo `rows`
fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
match a - b {
n if n < 0 => n + rows,
n => n
}
}
// disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert!(term.disp_srow() >= 0);
assert!(term.disp_erow() >= 0);
assert!(term.hist_srow() >= 0);
assert!(term.hist_erow() >= 0);
assert!(term.offset() >= 0);
assert!(term.disp_srow() <= ring_rows);
assert!(term.disp_erow() <= ring_rows);
assert!(term.hist_srow() <= ring_rows);
assert!(term.hist_erow() <= ring_rows);
assert!(term.offset() <= ring_rows);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
assert_eq!(term.ring_erow(), term.ring_rows() - 1);
assert_eq!(term.ring_srow(), 0);
// Check the different cols methods, which should all return the same answer
assert!(term.disp_cols() > 10);
assert_eq!(term.disp_cols(), term.ring_cols());
assert_eq!(term.disp_cols(), term.hist_cols());
// Redundant protected vs public methods:
assert_eq!(term.disp_cols(), term.display_columns());
assert_eq!(term.disp_rows(), term.display_rows());
/// Local function to read back all rows from the display into a long string.
/// Does not include scrollback history.
/// Trims trailing blanks on each line
fn read_disp(term: &Terminal) -> String {
let rows = term.display_rows();
let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
for row in 0..rows {
let r = term.u8c_disp_row(row).trim();
// Iterate through a row, accumulating [u8]
for c in r.iter() {
// Note: Sometimes utf-8 length is > 1
text.extend_from_slice(c.text_utf8());
}
text.extend_from_slice(b"\n");
}
// Return the result as a string
std::str::from_utf8(&text).unwrap().to_string()
}
term.clear();
term.append("Top line ↑ (up-arrow)");
term.set_text_attrib(Attrib::Underline);
term.append(" ");
term.set_text_attrib(Attrib::Normal);
term.append(" \n");
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
// The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
assert_eq!(text_out, "Top line ↑ (up-arrow) \n");
let r = term.u8c_disp_row(0);
assert_eq!(r.col(0).text_utf8(), b"T");
assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
let r = term.u8c_disp_row(1);
assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
// Clear the screen again, then append test text, then read it back and compare
let test_text =
"The wind was a torrent of darkness among the gusty trees.
The moon was a ghostly galleon tossed upon cloudy seas.
The road was a ribbon of moonlight over the purple moor,
And the highwayman came riding—
Riding—riding—
The highwayman came riding, up to the old inn-door.";
term.clear_history();
term.clear();
let bg_save = term.text_bg_color();
let fg_save = term.text_fg_color();
term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
term.append(test_text);
term.set_text_bg_color(bg_save);
term.set_text_fg_color(fg_save);
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
assert_eq!(test_text, text_out);
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
term.append(&format!(
"\n\nScreen has {} rows of {} columns.\n",
term.disp_rows(),
term.disp_cols()
));
term.set_text_attrib(Attrib::Italic);
term.append("Done!");
term.set_text_attrib(Attrib::Normal);
}
sourcepub fn hist_srow(&self) -> i32
pub fn hist_srow(&self) -> i32
Return the starting row# of the scrollback history.
Examples found in repository?
506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
// Test the Utf8Char primitive
let uc = Utf8Char::new(b'Q');
let uc1 = uc.text_utf8();
assert_eq!(&uc1, &[b'Q']);
assert_eq!(&uc.attrib(), &Attrib::Normal);
assert_eq!(
&uc.charflags(),
&(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
);
assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
let ring_rows = term.ring_rows();
// println!();
// dbg!(term.disp_srow(), term.disp_erow(), term.disp_rows(), term.ring_cols(), term.ring_srow(), term.ring_erow() );
// dbg!(term.hist_srow(), term.hist_erow(), term.hist_rows(), ring_rows );
// dbg!(term.offset(), term.hist_use_srow(), term.hist_use() );
term.take_focus().unwrap();
term.clear_history();
assert_eq!(term.history_use(), 0);
// Subtract row numbers, modulo `rows`
fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
match a - b {
n if n < 0 => n + rows,
n => n
}
}
// disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert!(term.disp_srow() >= 0);
assert!(term.disp_erow() >= 0);
assert!(term.hist_srow() >= 0);
assert!(term.hist_erow() >= 0);
assert!(term.offset() >= 0);
assert!(term.disp_srow() <= ring_rows);
assert!(term.disp_erow() <= ring_rows);
assert!(term.hist_srow() <= ring_rows);
assert!(term.hist_erow() <= ring_rows);
assert!(term.offset() <= ring_rows);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
assert_eq!(term.ring_erow(), term.ring_rows() - 1);
assert_eq!(term.ring_srow(), 0);
// Check the different cols methods, which should all return the same answer
assert!(term.disp_cols() > 10);
assert_eq!(term.disp_cols(), term.ring_cols());
assert_eq!(term.disp_cols(), term.hist_cols());
// Redundant protected vs public methods:
assert_eq!(term.disp_cols(), term.display_columns());
assert_eq!(term.disp_rows(), term.display_rows());
/// Local function to read back all rows from the display into a long string.
/// Does not include scrollback history.
/// Trims trailing blanks on each line
fn read_disp(term: &Terminal) -> String {
let rows = term.display_rows();
let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
for row in 0..rows {
let r = term.u8c_disp_row(row).trim();
// Iterate through a row, accumulating [u8]
for c in r.iter() {
// Note: Sometimes utf-8 length is > 1
text.extend_from_slice(c.text_utf8());
}
text.extend_from_slice(b"\n");
}
// Return the result as a string
std::str::from_utf8(&text).unwrap().to_string()
}
term.clear();
term.append("Top line ↑ (up-arrow)");
term.set_text_attrib(Attrib::Underline);
term.append(" ");
term.set_text_attrib(Attrib::Normal);
term.append(" \n");
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
// The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
assert_eq!(text_out, "Top line ↑ (up-arrow) \n");
let r = term.u8c_disp_row(0);
assert_eq!(r.col(0).text_utf8(), b"T");
assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
let r = term.u8c_disp_row(1);
assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
// Clear the screen again, then append test text, then read it back and compare
let test_text =
"The wind was a torrent of darkness among the gusty trees.
The moon was a ghostly galleon tossed upon cloudy seas.
The road was a ribbon of moonlight over the purple moor,
And the highwayman came riding—
Riding—riding—
The highwayman came riding, up to the old inn-door.";
term.clear_history();
term.clear();
let bg_save = term.text_bg_color();
let fg_save = term.text_fg_color();
term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
term.append(test_text);
term.set_text_bg_color(bg_save);
term.set_text_fg_color(fg_save);
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
assert_eq!(test_text, text_out);
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
term.append(&format!(
"\n\nScreen has {} rows of {} columns.\n",
term.disp_rows(),
term.disp_cols()
));
term.set_text_attrib(Attrib::Italic);
term.append("Done!");
term.set_text_attrib(Attrib::Normal);
}
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?
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?
506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
// Test the Utf8Char primitive
let uc = Utf8Char::new(b'Q');
let uc1 = uc.text_utf8();
assert_eq!(&uc1, &[b'Q']);
assert_eq!(&uc.attrib(), &Attrib::Normal);
assert_eq!(
&uc.charflags(),
&(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
);
assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
let ring_rows = term.ring_rows();
// println!();
// dbg!(term.disp_srow(), term.disp_erow(), term.disp_rows(), term.ring_cols(), term.ring_srow(), term.ring_erow() );
// dbg!(term.hist_srow(), term.hist_erow(), term.hist_rows(), ring_rows );
// dbg!(term.offset(), term.hist_use_srow(), term.hist_use() );
term.take_focus().unwrap();
term.clear_history();
assert_eq!(term.history_use(), 0);
// Subtract row numbers, modulo `rows`
fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
match a - b {
n if n < 0 => n + rows,
n => n
}
}
// disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert!(term.disp_srow() >= 0);
assert!(term.disp_erow() >= 0);
assert!(term.hist_srow() >= 0);
assert!(term.hist_erow() >= 0);
assert!(term.offset() >= 0);
assert!(term.disp_srow() <= ring_rows);
assert!(term.disp_erow() <= ring_rows);
assert!(term.hist_srow() <= ring_rows);
assert!(term.hist_erow() <= ring_rows);
assert!(term.offset() <= ring_rows);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
assert_eq!(term.ring_erow(), term.ring_rows() - 1);
assert_eq!(term.ring_srow(), 0);
// Check the different cols methods, which should all return the same answer
assert!(term.disp_cols() > 10);
assert_eq!(term.disp_cols(), term.ring_cols());
assert_eq!(term.disp_cols(), term.hist_cols());
// Redundant protected vs public methods:
assert_eq!(term.disp_cols(), term.display_columns());
assert_eq!(term.disp_rows(), term.display_rows());
/// Local function to read back all rows from the display into a long string.
/// Does not include scrollback history.
/// Trims trailing blanks on each line
fn read_disp(term: &Terminal) -> String {
let rows = term.display_rows();
let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
for row in 0..rows {
let r = term.u8c_disp_row(row).trim();
// Iterate through a row, accumulating [u8]
for c in r.iter() {
// Note: Sometimes utf-8 length is > 1
text.extend_from_slice(c.text_utf8());
}
text.extend_from_slice(b"\n");
}
// Return the result as a string
std::str::from_utf8(&text).unwrap().to_string()
}
term.clear();
term.append("Top line ↑ (up-arrow)");
term.set_text_attrib(Attrib::Underline);
term.append(" ");
term.set_text_attrib(Attrib::Normal);
term.append(" \n");
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
// The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
assert_eq!(text_out, "Top line ↑ (up-arrow) \n");
let r = term.u8c_disp_row(0);
assert_eq!(r.col(0).text_utf8(), b"T");
assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
let r = term.u8c_disp_row(1);
assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
// Clear the screen again, then append test text, then read it back and compare
let test_text =
"The wind was a torrent of darkness among the gusty trees.
The moon was a ghostly galleon tossed upon cloudy seas.
The road was a ribbon of moonlight over the purple moor,
And the highwayman came riding—
Riding—riding—
The highwayman came riding, up to the old inn-door.";
term.clear_history();
term.clear();
let bg_save = term.text_bg_color();
let fg_save = term.text_fg_color();
term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
term.append(test_text);
term.set_text_bg_color(bg_save);
term.set_text_fg_color(fg_save);
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
assert_eq!(test_text, text_out);
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
term.append(&format!(
"\n\nScreen has {} rows of {} columns.\n",
term.disp_rows(),
term.disp_cols()
));
term.set_text_attrib(Attrib::Italic);
term.append("Done!");
term.set_text_attrib(Attrib::Normal);
}
sourcepub fn ring_cols(&self) -> i32
pub fn ring_cols(&self) -> i32
Return the number of columns in the ring buffer.
Examples found in repository?
506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
// Test the Utf8Char primitive
let uc = Utf8Char::new(b'Q');
let uc1 = uc.text_utf8();
assert_eq!(&uc1, &[b'Q']);
assert_eq!(&uc.attrib(), &Attrib::Normal);
assert_eq!(
&uc.charflags(),
&(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
);
assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
let ring_rows = term.ring_rows();
// println!();
// dbg!(term.disp_srow(), term.disp_erow(), term.disp_rows(), term.ring_cols(), term.ring_srow(), term.ring_erow() );
// dbg!(term.hist_srow(), term.hist_erow(), term.hist_rows(), ring_rows );
// dbg!(term.offset(), term.hist_use_srow(), term.hist_use() );
term.take_focus().unwrap();
term.clear_history();
assert_eq!(term.history_use(), 0);
// Subtract row numbers, modulo `rows`
fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
match a - b {
n if n < 0 => n + rows,
n => n
}
}
// disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert!(term.disp_srow() >= 0);
assert!(term.disp_erow() >= 0);
assert!(term.hist_srow() >= 0);
assert!(term.hist_erow() >= 0);
assert!(term.offset() >= 0);
assert!(term.disp_srow() <= ring_rows);
assert!(term.disp_erow() <= ring_rows);
assert!(term.hist_srow() <= ring_rows);
assert!(term.hist_erow() <= ring_rows);
assert!(term.offset() <= ring_rows);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
assert_eq!(term.ring_erow(), term.ring_rows() - 1);
assert_eq!(term.ring_srow(), 0);
// Check the different cols methods, which should all return the same answer
assert!(term.disp_cols() > 10);
assert_eq!(term.disp_cols(), term.ring_cols());
assert_eq!(term.disp_cols(), term.hist_cols());
// Redundant protected vs public methods:
assert_eq!(term.disp_cols(), term.display_columns());
assert_eq!(term.disp_rows(), term.display_rows());
/// Local function to read back all rows from the display into a long string.
/// Does not include scrollback history.
/// Trims trailing blanks on each line
fn read_disp(term: &Terminal) -> String {
let rows = term.display_rows();
let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
for row in 0..rows {
let r = term.u8c_disp_row(row).trim();
// Iterate through a row, accumulating [u8]
for c in r.iter() {
// Note: Sometimes utf-8 length is > 1
text.extend_from_slice(c.text_utf8());
}
text.extend_from_slice(b"\n");
}
// Return the result as a string
std::str::from_utf8(&text).unwrap().to_string()
}
term.clear();
term.append("Top line ↑ (up-arrow)");
term.set_text_attrib(Attrib::Underline);
term.append(" ");
term.set_text_attrib(Attrib::Normal);
term.append(" \n");
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
// The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
assert_eq!(text_out, "Top line ↑ (up-arrow) \n");
let r = term.u8c_disp_row(0);
assert_eq!(r.col(0).text_utf8(), b"T");
assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
let r = term.u8c_disp_row(1);
assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
// Clear the screen again, then append test text, then read it back and compare
let test_text =
"The wind was a torrent of darkness among the gusty trees.
The moon was a ghostly galleon tossed upon cloudy seas.
The road was a ribbon of moonlight over the purple moor,
And the highwayman came riding—
Riding—riding—
The highwayman came riding, up to the old inn-door.";
term.clear_history();
term.clear();
let bg_save = term.text_bg_color();
let fg_save = term.text_fg_color();
term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
term.append(test_text);
term.set_text_bg_color(bg_save);
term.set_text_fg_color(fg_save);
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
assert_eq!(test_text, text_out);
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
term.append(&format!(
"\n\nScreen has {} rows of {} columns.\n",
term.disp_rows(),
term.disp_cols()
));
term.set_text_attrib(Attrib::Italic);
term.append("Done!");
term.set_text_attrib(Attrib::Normal);
}
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?
506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
// Test the Utf8Char primitive
let uc = Utf8Char::new(b'Q');
let uc1 = uc.text_utf8();
assert_eq!(&uc1, &[b'Q']);
assert_eq!(&uc.attrib(), &Attrib::Normal);
assert_eq!(
&uc.charflags(),
&(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
);
assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
let ring_rows = term.ring_rows();
// println!();
// dbg!(term.disp_srow(), term.disp_erow(), term.disp_rows(), term.ring_cols(), term.ring_srow(), term.ring_erow() );
// dbg!(term.hist_srow(), term.hist_erow(), term.hist_rows(), ring_rows );
// dbg!(term.offset(), term.hist_use_srow(), term.hist_use() );
term.take_focus().unwrap();
term.clear_history();
assert_eq!(term.history_use(), 0);
// Subtract row numbers, modulo `rows`
fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
match a - b {
n if n < 0 => n + rows,
n => n
}
}
// disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert!(term.disp_srow() >= 0);
assert!(term.disp_erow() >= 0);
assert!(term.hist_srow() >= 0);
assert!(term.hist_erow() >= 0);
assert!(term.offset() >= 0);
assert!(term.disp_srow() <= ring_rows);
assert!(term.disp_erow() <= ring_rows);
assert!(term.hist_srow() <= ring_rows);
assert!(term.hist_erow() <= ring_rows);
assert!(term.offset() <= ring_rows);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
assert_eq!(term.ring_erow(), term.ring_rows() - 1);
assert_eq!(term.ring_srow(), 0);
// Check the different cols methods, which should all return the same answer
assert!(term.disp_cols() > 10);
assert_eq!(term.disp_cols(), term.ring_cols());
assert_eq!(term.disp_cols(), term.hist_cols());
// Redundant protected vs public methods:
assert_eq!(term.disp_cols(), term.display_columns());
assert_eq!(term.disp_rows(), term.display_rows());
/// Local function to read back all rows from the display into a long string.
/// Does not include scrollback history.
/// Trims trailing blanks on each line
fn read_disp(term: &Terminal) -> String {
let rows = term.display_rows();
let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
for row in 0..rows {
let r = term.u8c_disp_row(row).trim();
// Iterate through a row, accumulating [u8]
for c in r.iter() {
// Note: Sometimes utf-8 length is > 1
text.extend_from_slice(c.text_utf8());
}
text.extend_from_slice(b"\n");
}
// Return the result as a string
std::str::from_utf8(&text).unwrap().to_string()
}
term.clear();
term.append("Top line ↑ (up-arrow)");
term.set_text_attrib(Attrib::Underline);
term.append(" ");
term.set_text_attrib(Attrib::Normal);
term.append(" \n");
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
// The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
assert_eq!(text_out, "Top line ↑ (up-arrow) \n");
let r = term.u8c_disp_row(0);
assert_eq!(r.col(0).text_utf8(), b"T");
assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
let r = term.u8c_disp_row(1);
assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
// Clear the screen again, then append test text, then read it back and compare
let test_text =
"The wind was a torrent of darkness among the gusty trees.
The moon was a ghostly galleon tossed upon cloudy seas.
The road was a ribbon of moonlight over the purple moor,
And the highwayman came riding—
Riding—riding—
The highwayman came riding, up to the old inn-door.";
term.clear_history();
term.clear();
let bg_save = term.text_bg_color();
let fg_save = term.text_fg_color();
term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
term.append(test_text);
term.set_text_bg_color(bg_save);
term.set_text_fg_color(fg_save);
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
assert_eq!(test_text, text_out);
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
term.append(&format!(
"\n\nScreen has {} rows of {} columns.\n",
term.disp_rows(),
term.disp_cols()
));
term.set_text_attrib(Attrib::Italic);
term.append("Done!");
term.set_text_attrib(Attrib::Normal);
}
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?
506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
// Test the Utf8Char primitive
let uc = Utf8Char::new(b'Q');
let uc1 = uc.text_utf8();
assert_eq!(&uc1, &[b'Q']);
assert_eq!(&uc.attrib(), &Attrib::Normal);
assert_eq!(
&uc.charflags(),
&(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
);
assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
let ring_rows = term.ring_rows();
// println!();
// dbg!(term.disp_srow(), term.disp_erow(), term.disp_rows(), term.ring_cols(), term.ring_srow(), term.ring_erow() );
// dbg!(term.hist_srow(), term.hist_erow(), term.hist_rows(), ring_rows );
// dbg!(term.offset(), term.hist_use_srow(), term.hist_use() );
term.take_focus().unwrap();
term.clear_history();
assert_eq!(term.history_use(), 0);
// Subtract row numbers, modulo `rows`
fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
match a - b {
n if n < 0 => n + rows,
n => n
}
}
// disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert!(term.disp_srow() >= 0);
assert!(term.disp_erow() >= 0);
assert!(term.hist_srow() >= 0);
assert!(term.hist_erow() >= 0);
assert!(term.offset() >= 0);
assert!(term.disp_srow() <= ring_rows);
assert!(term.disp_erow() <= ring_rows);
assert!(term.hist_srow() <= ring_rows);
assert!(term.hist_erow() <= ring_rows);
assert!(term.offset() <= ring_rows);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
assert_eq!(term.ring_erow(), term.ring_rows() - 1);
assert_eq!(term.ring_srow(), 0);
// Check the different cols methods, which should all return the same answer
assert!(term.disp_cols() > 10);
assert_eq!(term.disp_cols(), term.ring_cols());
assert_eq!(term.disp_cols(), term.hist_cols());
// Redundant protected vs public methods:
assert_eq!(term.disp_cols(), term.display_columns());
assert_eq!(term.disp_rows(), term.display_rows());
/// Local function to read back all rows from the display into a long string.
/// Does not include scrollback history.
/// Trims trailing blanks on each line
fn read_disp(term: &Terminal) -> String {
let rows = term.display_rows();
let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
for row in 0..rows {
let r = term.u8c_disp_row(row).trim();
// Iterate through a row, accumulating [u8]
for c in r.iter() {
// Note: Sometimes utf-8 length is > 1
text.extend_from_slice(c.text_utf8());
}
text.extend_from_slice(b"\n");
}
// Return the result as a string
std::str::from_utf8(&text).unwrap().to_string()
}
term.clear();
term.append("Top line ↑ (up-arrow)");
term.set_text_attrib(Attrib::Underline);
term.append(" ");
term.set_text_attrib(Attrib::Normal);
term.append(" \n");
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
// The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
assert_eq!(text_out, "Top line ↑ (up-arrow) \n");
let r = term.u8c_disp_row(0);
assert_eq!(r.col(0).text_utf8(), b"T");
assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
let r = term.u8c_disp_row(1);
assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
// Clear the screen again, then append test text, then read it back and compare
let test_text =
"The wind was a torrent of darkness among the gusty trees.
The moon was a ghostly galleon tossed upon cloudy seas.
The road was a ribbon of moonlight over the purple moor,
And the highwayman came riding—
Riding—riding—
The highwayman came riding, up to the old inn-door.";
term.clear_history();
term.clear();
let bg_save = term.text_bg_color();
let fg_save = term.text_fg_color();
term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
term.append(test_text);
term.set_text_bg_color(bg_save);
term.set_text_fg_color(fg_save);
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
assert_eq!(test_text, text_out);
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
term.append(&format!(
"\n\nScreen has {} rows of {} columns.\n",
term.disp_rows(),
term.disp_cols()
));
term.set_text_attrib(Attrib::Italic);
term.append("Done!");
term.set_text_attrib(Attrib::Normal);
}
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?
506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
// Test the Utf8Char primitive
let uc = Utf8Char::new(b'Q');
let uc1 = uc.text_utf8();
assert_eq!(&uc1, &[b'Q']);
assert_eq!(&uc.attrib(), &Attrib::Normal);
assert_eq!(
&uc.charflags(),
&(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
);
assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
let ring_rows = term.ring_rows();
// println!();
// dbg!(term.disp_srow(), term.disp_erow(), term.disp_rows(), term.ring_cols(), term.ring_srow(), term.ring_erow() );
// dbg!(term.hist_srow(), term.hist_erow(), term.hist_rows(), ring_rows );
// dbg!(term.offset(), term.hist_use_srow(), term.hist_use() );
term.take_focus().unwrap();
term.clear_history();
assert_eq!(term.history_use(), 0);
// Subtract row numbers, modulo `rows`
fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
match a - b {
n if n < 0 => n + rows,
n => n
}
}
// disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert!(term.disp_srow() >= 0);
assert!(term.disp_erow() >= 0);
assert!(term.hist_srow() >= 0);
assert!(term.hist_erow() >= 0);
assert!(term.offset() >= 0);
assert!(term.disp_srow() <= ring_rows);
assert!(term.disp_erow() <= ring_rows);
assert!(term.hist_srow() <= ring_rows);
assert!(term.hist_erow() <= ring_rows);
assert!(term.offset() <= ring_rows);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
assert_eq!(term.ring_erow(), term.ring_rows() - 1);
assert_eq!(term.ring_srow(), 0);
// Check the different cols methods, which should all return the same answer
assert!(term.disp_cols() > 10);
assert_eq!(term.disp_cols(), term.ring_cols());
assert_eq!(term.disp_cols(), term.hist_cols());
// Redundant protected vs public methods:
assert_eq!(term.disp_cols(), term.display_columns());
assert_eq!(term.disp_rows(), term.display_rows());
/// Local function to read back all rows from the display into a long string.
/// Does not include scrollback history.
/// Trims trailing blanks on each line
fn read_disp(term: &Terminal) -> String {
let rows = term.display_rows();
let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
for row in 0..rows {
let r = term.u8c_disp_row(row).trim();
// Iterate through a row, accumulating [u8]
for c in r.iter() {
// Note: Sometimes utf-8 length is > 1
text.extend_from_slice(c.text_utf8());
}
text.extend_from_slice(b"\n");
}
// Return the result as a string
std::str::from_utf8(&text).unwrap().to_string()
}
term.clear();
term.append("Top line ↑ (up-arrow)");
term.set_text_attrib(Attrib::Underline);
term.append(" ");
term.set_text_attrib(Attrib::Normal);
term.append(" \n");
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
// The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
assert_eq!(text_out, "Top line ↑ (up-arrow) \n");
let r = term.u8c_disp_row(0);
assert_eq!(r.col(0).text_utf8(), b"T");
assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
let r = term.u8c_disp_row(1);
assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
// Clear the screen again, then append test text, then read it back and compare
let test_text =
"The wind was a torrent of darkness among the gusty trees.
The moon was a ghostly galleon tossed upon cloudy seas.
The road was a ribbon of moonlight over the purple moor,
And the highwayman came riding—
Riding—riding—
The highwayman came riding, up to the old inn-door.";
term.clear_history();
term.clear();
let bg_save = term.text_bg_color();
let fg_save = term.text_fg_color();
term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
term.append(test_text);
term.set_text_bg_color(bg_save);
term.set_text_fg_color(fg_save);
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
assert_eq!(test_text, text_out);
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
term.append(&format!(
"\n\nScreen has {} rows of {} columns.\n",
term.disp_rows(),
term.disp_cols()
));
term.set_text_attrib(Attrib::Italic);
term.append("Done!");
term.set_text_attrib(Attrib::Normal);
}
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.
Examples found in repository?
506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
// Test the Utf8Char primitive
let uc = Utf8Char::new(b'Q');
let uc1 = uc.text_utf8();
assert_eq!(&uc1, &[b'Q']);
assert_eq!(&uc.attrib(), &Attrib::Normal);
assert_eq!(
&uc.charflags(),
&(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
);
assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
let ring_rows = term.ring_rows();
// println!();
// dbg!(term.disp_srow(), term.disp_erow(), term.disp_rows(), term.ring_cols(), term.ring_srow(), term.ring_erow() );
// dbg!(term.hist_srow(), term.hist_erow(), term.hist_rows(), ring_rows );
// dbg!(term.offset(), term.hist_use_srow(), term.hist_use() );
term.take_focus().unwrap();
term.clear_history();
assert_eq!(term.history_use(), 0);
// Subtract row numbers, modulo `rows`
fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
match a - b {
n if n < 0 => n + rows,
n => n
}
}
// disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert!(term.disp_srow() >= 0);
assert!(term.disp_erow() >= 0);
assert!(term.hist_srow() >= 0);
assert!(term.hist_erow() >= 0);
assert!(term.offset() >= 0);
assert!(term.disp_srow() <= ring_rows);
assert!(term.disp_erow() <= ring_rows);
assert!(term.hist_srow() <= ring_rows);
assert!(term.hist_erow() <= ring_rows);
assert!(term.offset() <= ring_rows);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
assert_eq!(term.ring_erow(), term.ring_rows() - 1);
assert_eq!(term.ring_srow(), 0);
// Check the different cols methods, which should all return the same answer
assert!(term.disp_cols() > 10);
assert_eq!(term.disp_cols(), term.ring_cols());
assert_eq!(term.disp_cols(), term.hist_cols());
// Redundant protected vs public methods:
assert_eq!(term.disp_cols(), term.display_columns());
assert_eq!(term.disp_rows(), term.display_rows());
/// Local function to read back all rows from the display into a long string.
/// Does not include scrollback history.
/// Trims trailing blanks on each line
fn read_disp(term: &Terminal) -> String {
let rows = term.display_rows();
let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
for row in 0..rows {
let r = term.u8c_disp_row(row).trim();
// Iterate through a row, accumulating [u8]
for c in r.iter() {
// Note: Sometimes utf-8 length is > 1
text.extend_from_slice(c.text_utf8());
}
text.extend_from_slice(b"\n");
}
// Return the result as a string
std::str::from_utf8(&text).unwrap().to_string()
}
term.clear();
term.append("Top line ↑ (up-arrow)");
term.set_text_attrib(Attrib::Underline);
term.append(" ");
term.set_text_attrib(Attrib::Normal);
term.append(" \n");
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
// The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
assert_eq!(text_out, "Top line ↑ (up-arrow) \n");
let r = term.u8c_disp_row(0);
assert_eq!(r.col(0).text_utf8(), b"T");
assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
let r = term.u8c_disp_row(1);
assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
// Clear the screen again, then append test text, then read it back and compare
let test_text =
"The wind was a torrent of darkness among the gusty trees.
The moon was a ghostly galleon tossed upon cloudy seas.
The road was a ribbon of moonlight over the purple moor,
And the highwayman came riding—
Riding—riding—
The highwayman came riding, up to the old inn-door.";
term.clear_history();
term.clear();
let bg_save = term.text_bg_color();
let fg_save = term.text_fg_color();
term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
term.append(test_text);
term.set_text_bg_color(bg_save);
term.set_text_fg_color(fg_save);
let mut text_out = read_disp(term);
// Trim trailing empty lines
text_out = text_out.trim_end_matches(&"\n\n").to_string();
assert_eq!(test_text, text_out);
assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
assert_eq!(term.ring_srow(), 0);
assert_eq!(term.ring_erow(), ring_rows - 1);
assert_eq!(
row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
term.disp_rows()
);
assert_eq!(
row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
term.hist_rows()
);
term.append(&format!(
"\n\nScreen has {} rows of {} columns.\n",
term.disp_rows(),
term.disp_cols()
));
term.set_text_attrib(Attrib::Italic);
term.append("Done!");
term.set_text_attrib(Attrib::Normal);
}
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.
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.
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.
Trait Implementations§
source§impl GroupExt for Terminal
impl GroupExt for Terminal
source§fn find<W: WidgetExt>(&self, widget: &W) -> i32
fn find<W: WidgetExt>(&self, widget: &W) -> i32
source§fn insert<W: WidgetExt>(&mut self, widget: &W, index: i32)
fn insert<W: WidgetExt>(&mut self, widget: &W, index: i32)
source§fn remove<W: WidgetExt>(&mut self, widget: &W)
fn remove<W: WidgetExt>(&mut self, widget: &W)
source§fn remove_by_index(&mut self, idx: i32)
fn remove_by_index(&mut self, idx: i32)
source§fn resizable<W: WidgetExt>(&self, widget: &W)
fn resizable<W: WidgetExt>(&self, widget: &W)
source§fn make_resizable(&mut self, val: bool)
fn make_resizable(&mut self, val: bool)
source§fn add_resizable<W: WidgetExt>(&mut self, widget: &W)
fn add_resizable<W: WidgetExt>(&mut self, widget: &W)
source§fn set_clip_children(&mut self, flag: bool)
fn set_clip_children(&mut self, flag: bool)
source§fn clip_children(&self) -> bool
fn clip_children(&self) -> bool
clip_children
is setsource§fn draw_child<W: WidgetExt>(&self, w: &mut W)
fn draw_child<W: WidgetExt>(&self, w: &mut W)
WidgetBase::draw
methodsource§fn update_child<W: WidgetExt>(&self, w: &mut W)
fn update_child<W: WidgetExt>(&self, w: &mut W)
WidgetBase::draw
methodsource§fn draw_outside_label<W: WidgetExt>(&self, w: &mut W)
fn draw_outside_label<W: WidgetExt>(&self, w: &mut W)
WidgetBase::draw
methodsource§fn draw_children(&mut self)
fn draw_children(&mut self)
WidgetBase::draw
methodsource§fn init_sizes(&mut self)
fn init_sizes(&mut self)
source§fn bounds(&self) -> Vec<(i32, i32, i32, i32)>
fn bounds(&self) -> Vec<(i32, i32, i32, i32)>
source§unsafe fn into_group(&self) -> Group
unsafe fn into_group(&self) -> Group
source§impl IntoIterator for Terminal
impl IntoIterator for Terminal
source§impl PartialEq for Terminal
impl PartialEq for Terminal
source§impl WidgetBase for Terminal
impl WidgetBase for Terminal
source§fn new<'a, T: Into<Option<&'a str>>>(
x: i32,
y: i32,
width: i32,
height: i32,
title: T
) -> Terminal
fn new<'a, T: Into<Option<&'a str>>>( x: i32, y: i32, width: i32, height: i32, title: T ) -> Terminal
source§fn default_fill() -> Self
fn default_fill() -> Self
source§unsafe fn from_widget_ptr(ptr: *mut Fl_Widget) -> Self
unsafe fn from_widget_ptr(ptr: *mut Fl_Widget) -> Self
source§unsafe fn from_widget<W: WidgetExt>(w: W) -> Self
unsafe fn from_widget<W: WidgetExt>(w: W) -> Self
source§fn handle<F: FnMut(&mut Self, Event) -> bool + 'static>(&mut self, cb: F)
fn handle<F: FnMut(&mut Self, Event) -> bool + 'static>(&mut self, cb: F)
Fl_Widget::handle(int)
.
Handled or ignored events should return true, unhandled events should return false.
takes the widget as a closure argument.
The ability to handle an event might depend on handling other events, as explained heresource§fn draw<F: FnMut(&mut Self) + 'static>(&mut self, cb: F)
fn draw<F: FnMut(&mut Self) + 'static>(&mut self, cb: F)
WidgetBase::draw
actually calls drawing functionssource§fn resize_callback<F: FnMut(&mut Self, i32, i32, i32, i32) + 'static>(
&mut self,
cb: F
)
fn resize_callback<F: FnMut(&mut Self, i32, i32, i32, i32) + 'static>( &mut self, cb: F )
source§unsafe fn assume_derived(&mut self)
unsafe fn assume_derived(&mut self)
source§impl WidgetExt for Terminal
impl WidgetExt for Terminal
source§fn with_label(self, title: &str) -> Self
fn with_label(self, title: &str) -> Self
source§fn with_align(self, align: Align) -> Self
fn with_align(self, align: Align) -> Self
source§fn with_type<T: WidgetType>(self, typ: T) -> Self
fn with_type<T: WidgetType>(self, typ: T) -> Self
source§fn below_of<W: WidgetExt>(self, wid: &W, padding: i32) -> Self
fn below_of<W: WidgetExt>(self, wid: &W, padding: i32) -> Self
source§fn above_of<W: WidgetExt>(self, wid: &W, padding: i32) -> Self
fn above_of<W: WidgetExt>(self, wid: &W, padding: i32) -> Self
source§fn right_of<W: WidgetExt>(self, wid: &W, padding: i32) -> Self
fn right_of<W: WidgetExt>(self, wid: &W, padding: i32) -> Self
source§fn left_of<W: WidgetExt>(self, wid: &W, padding: i32) -> Self
fn left_of<W: WidgetExt>(self, wid: &W, padding: i32) -> Self
source§fn center_of_parent(self) -> Self
fn center_of_parent(self) -> Self
source§fn size_of_parent(self) -> Self
fn size_of_parent(self) -> Self
source§fn set_label(&mut self, title: &str)
fn set_label(&mut self, title: &str)
@
sign.
and for the associated formatting.source§fn measure_label(&self) -> (i32, i32)
fn measure_label(&self) -> (i32, i32)
source§fn as_widget_ptr(&self) -> *mut Fl_Widget
fn as_widget_ptr(&self) -> *mut Fl_Widget
Fl_Widget
, for internal usesource§fn deactivate(&mut self)
fn deactivate(&mut self)
source§fn redraw_label(&mut self)
fn redraw_label(&mut self)
source§fn resize(&mut self, x: i32, y: i32, width: i32, height: i32)
fn resize(&mut self, x: i32, y: i32, width: i32, height: i32)
source§fn widget_resize(&mut self, x: i32, y: i32, width: i32, height: i32)
fn widget_resize(&mut self, x: i32, y: i32, width: i32, height: i32)
source§fn set_tooltip(&mut self, txt: &str)
fn set_tooltip(&mut self, txt: &str)
source§fn label_color(&self) -> Color
fn label_color(&self) -> Color
source§fn set_label_color(&mut self, color: Color)
fn set_label_color(&mut self, color: Color)
source§fn label_font(&self) -> Font
fn label_font(&self) -> Font
source§fn set_label_font(&mut self, font: Font)
fn set_label_font(&mut self, font: Font)
source§fn label_size(&self) -> i32
fn label_size(&self) -> i32
source§fn set_label_size(&mut self, sz: i32)
fn set_label_size(&mut self, sz: i32)
source§fn label_type(&self) -> LabelType
fn label_type(&self) -> LabelType
source§fn set_label_type(&mut self, typ: LabelType)
fn set_label_type(&mut self, typ: LabelType)
source§fn set_changed(&mut self)
fn set_changed(&mut self)
source§fn clear_changed(&mut self)
fn clear_changed(&mut self)
source§fn set_trigger(&mut self, trigger: CallbackTrigger)
fn set_trigger(&mut self, trigger: CallbackTrigger)
when()
source§fn trigger(&self) -> CallbackTrigger
fn trigger(&self) -> CallbackTrigger
when()
source§fn selection_color(&self) -> Color
fn selection_color(&self) -> Color
source§fn set_selection_color(&mut self, color: Color)
fn set_selection_color(&mut self, color: Color)
source§fn do_callback(&mut self)
fn do_callback(&mut self)
source§fn top_window(&self) -> Option<Box<dyn WindowExt>>
fn top_window(&self) -> Option<Box<dyn WindowExt>>
source§fn takes_events(&self) -> bool
fn takes_events(&self) -> bool
source§fn set_visible_focus(&mut self)
fn set_visible_focus(&mut self)
source§fn clear_visible_focus(&mut self)
fn clear_visible_focus(&mut self)
source§fn visible_focus(&mut self, v: bool)
fn visible_focus(&mut self, v: bool)
source§fn has_visible_focus(&self) -> bool
fn has_visible_focus(&self) -> bool
source§fn was_deleted(&self) -> bool
fn was_deleted(&self) -> bool
source§fn set_damage(&mut self, flag: bool)
fn set_damage(&mut self, flag: bool)
source§fn damage_type(&self) -> Damage
fn damage_type(&self) -> Damage
source§fn set_damage_type(&mut self, mask: Damage)
fn set_damage_type(&mut self, mask: Damage)
source§fn set_damage_area(&mut self, mask: Damage, x: i32, y: i32, w: i32, h: i32)
fn set_damage_area(&mut self, mask: Damage, x: i32, y: i32, w: i32, h: i32)
source§fn clear_damage(&mut self)
fn clear_damage(&mut self)
source§fn as_window(&self) -> Option<Box<dyn WindowExt>>
fn as_window(&self) -> Option<Box<dyn WindowExt>>
source§fn as_group(&self) -> Option<Group>
fn as_group(&self) -> Option<Group>
source§fn inside<W: WidgetExt>(&self, wid: &W) -> bool
fn inside<W: WidgetExt>(&self, wid: &W) -> bool
source§fn get_type<T: WidgetType>(&self) -> T
fn get_type<T: WidgetType>(&self) -> T
source§fn set_type<T: WidgetType>(&mut self, typ: T)
fn set_type<T: WidgetType>(&mut self, typ: T)
source§fn set_image_scaled<I: ImageExt>(&mut self, image: Option<I>)
fn set_image_scaled<I: ImageExt>(&mut self, image: Option<I>)
source§unsafe fn image_mut(&self) -> Option<&mut Image>
unsafe fn image_mut(&self) -> Option<&mut Image>
source§fn set_deimage<I: ImageExt>(&mut self, image: Option<I>)
fn set_deimage<I: ImageExt>(&mut self, image: Option<I>)
source§fn set_deimage_scaled<I: ImageExt>(&mut self, image: Option<I>)
fn set_deimage_scaled<I: ImageExt>(&mut self, image: Option<I>)
source§fn deimage(&self) -> Option<Box<dyn ImageExt>>
fn deimage(&self) -> Option<Box<dyn ImageExt>>
source§unsafe fn deimage_mut(&self) -> Option<&mut Image>
unsafe fn deimage_mut(&self) -> Option<&mut Image>
source§fn set_callback<F: FnMut(&mut Self) + 'static>(&mut self, cb: F)
fn set_callback<F: FnMut(&mut Self) + 'static>(&mut self, cb: F)
source§fn emit<T: 'static + Clone + Send + Sync>(&mut self, sender: Sender<T>, msg: T)
fn emit<T: 'static + Clone + Send + Sync>(&mut self, sender: Sender<T>, msg: T)
source§unsafe fn into_widget<W: WidgetBase>(&self) -> W
unsafe fn into_widget<W: WidgetBase>(&self) -> W
WidgetExt
to some widget type Read moresource§fn visible_r(&self) -> bool
fn visible_r(&self) -> bool
source§fn is_same<W: WidgetExt>(&self, other: &W) -> bool
fn is_same<W: WidgetExt>(&self, other: &W) -> bool
source§fn active_r(&self) -> bool
fn active_r(&self) -> bool
source§fn handle_event(&mut self, event: Event) -> bool
fn handle_event(&mut self, event: Event) -> bool
source§fn is_derived(&self) -> bool
fn is_derived(&self) -> bool
source§fn as_base_widget(&self) -> Widgetwhere
Self: Sized,
fn as_base_widget(&self) -> Widgetwhere
Self: Sized,
WidgetExt
to a Widgetimpl Eq for Terminal
impl Send for Terminal
single-threaded
only.impl Sync for Terminal
single-threaded
only.