pub struct Color { /* private fields */ }Expand description
Defines colors used by FLTK.
Colors are stored as RGBI values, the last being the index for FLTK colors in this enum.
Colors in this enum don’t have an RGB stored. However, custom colors have an RGB, and don’t have an index.
The RGBI can be acquired by casting the color to u32 and formatting it to 0x{08x}.
The last 2 digits are the hexadecimal representation of the color in this enum.
For example, Color::White, has a hex of 0x000000ff, ff being the 255 value of this enum.
A custom color like Color::from_u32(0x646464), will have an representation as 0x64646400,
of which the final 00 indicates that it is not stored in this enum.
For convenience, the fmt::Display trait is implemented so that the name of the Color is shown
when there is one, otherwise the RGB value is given.
Implementations§
Source§impl Color
impl Color
Sourcepub const ForeGround: Color
pub const ForeGround: Color
ForeGround, label colors
Sourcepub const Foreground: Color
pub const Foreground: Color
Foreground, label colors
Sourcepub const BackGround2: Color
pub const BackGround2: Color
BackGround2, Is the color inside input, output and text display widgets
Sourcepub const Background2: Color
pub const Background2: Color
Background2, Is the color inside input, output and text display widgets
Sourcepub const FrameDefault: Color
pub const FrameDefault: Color
FrameDefault
Sourcepub const BackGround: Color
pub const BackGround: Color
BackGround
Sourcepub const Background: Color
pub const Background: Color
Background
Sourcepub const DarkYellow: Color
pub const DarkYellow: Color
DarkYellow
Sourcepub const DarkMagenta: Color
pub const DarkMagenta: Color
DarkMagenta
Sourcepub const XtermBlack: Color
pub const XtermBlack: Color
ANSI/xterm Black, not part of FLTK’s colormap
Sourcepub const XtermGreen: Color
pub const XtermGreen: Color
ANSI/xterm Green, not part of FLTK’s colormap
Sourcepub const XtermYellow: Color
pub const XtermYellow: Color
ANSI/xterm Yellow, not part of FLTK’s colormap
Sourcepub const XtermMagenta: Color
pub const XtermMagenta: Color
ANSI/xterm Magenta, not part of FLTK’s colormap
Sourcepub const XtermWhite: Color
pub const XtermWhite: Color
ANSI/xterm White, not part of FLTK’s colormap
Sourcepub const XtermBgRed: Color
pub const XtermBgRed: Color
ANSI/xterm background Red, not part of FLTK’s colormap
Sourcepub const XtermBgGreen: Color
pub const XtermBgGreen: Color
ANSI/xterm background Green, not part of FLTK’s colormap
Sourcepub const XtermBgYellow: Color
pub const XtermBgYellow: Color
ANSI/xterm background Yelllow, not part of FLTK’s colormap
Sourcepub const XtermBgBlue: Color
pub const XtermBgBlue: Color
ANSI/xterm background Blue, not part of FLTK’s colormap
Sourcepub const XtermBgMagenta: Color
pub const XtermBgMagenta: Color
ANSI/xterm background Magenta, not part of FLTK’s colormap
Sourcepub const XtermBgCyan: Color
pub const XtermBgCyan: Color
ANSI/xterm background Cyan, not part of FLTK’s colormap
Sourcepub const XtermBgWhite: Color
pub const XtermBgWhite: Color
ANSI/xterm background White, not part of FLTK’s colormap
Sourcepub const TransparentBg: Color
pub const TransparentBg: Color
Special background color value that lets the Terminal widget’s box() color show through behind the text. Not part of FLTK’s colormap
Sourcepub const fn from_rgb(r: u8, g: u8, b: u8) -> Color
pub const fn from_rgb(r: u8, g: u8, b: u8) -> Color
Returns a color from RGB
Examples found in repository?
3fn main() {
4 let a = app::App::default().with_scheme(app::Scheme::Gtk);
5 let mut win = window::Window::default().with_size(640, 480);
6 let mut col = group::Flex::default_fill().column();
7 main_panel(&mut col);
8 col.end();
9 win.resizable(&col);
10 win.set_color(enums::Color::from_rgb(250, 250, 250));
11 win.end();
12 win.show();
13 win.size_range(600, 400, 0, 0);
14 a.run().unwrap();
15}
16
17fn buttons_panel(parent: &mut group::Flex) {
18 frame::Frame::default();
19 let w = frame::Frame::default().with_label("Welcome to Flex Login");
20
21 let mut urow = group::Flex::default().row();
22 {
23 frame::Frame::default()
24 .with_label("Username:")
25 .with_align(enums::Align::Inside | enums::Align::Right);
26 let username = input::Input::default();
27
28 urow.fixed(&username, 180);
29 urow.end();
30 }
31
32 let mut prow = group::Flex::default().row();
33 {
34 frame::Frame::default()
35 .with_label("Password:")
36 .with_align(enums::Align::Inside | enums::Align::Right);
37 let password = input::Input::default();
38
39 prow.fixed(&password, 180);
40 prow.end();
41 }
42
43 let pad = frame::Frame::default();
44
45 let mut brow = group::Flex::default().row();
46 {
47 frame::Frame::default();
48 let reg = create_button("Register");
49 let login = create_button("Login");
50
51 brow.fixed(®, 80);
52 brow.fixed(&login, 80);
53 brow.end();
54 }
55
56 let b = frame::Frame::default();
57
58 frame::Frame::default();
59
60 parent.fixed(&w, 60);
61 parent.fixed(&urow, 30);
62 parent.fixed(&prow, 30);
63 parent.fixed(&pad, 1);
64 parent.fixed(&brow, 30);
65 parent.fixed(&b, 30);
66}
67
68fn middle_panel(parent: &mut group::Flex) {
69 frame::Frame::default();
70
71 let mut frame = frame::Frame::default().with_label("Image");
72 frame.set_frame(enums::FrameType::BorderBox);
73 frame.set_color(enums::Color::from_rgb(0, 200, 0));
74 let spacer = frame::Frame::default();
75
76 let mut bp = group::Flex::default().column();
77 buttons_panel(&mut bp);
78 bp.end();
79
80 frame::Frame::default();
81
82 parent.fixed(&frame, 200);
83 parent.fixed(&spacer, 10);
84 parent.fixed(&bp, 300);
85}
86
87fn main_panel(parent: &mut group::Flex) {
88 frame::Frame::default();
89
90 let mut mp = group::Flex::default().row();
91 middle_panel(&mut mp);
92 mp.end();
93
94 frame::Frame::default();
95
96 parent.fixed(&mp, 200);
97}
98
99fn create_button(caption: &str) -> button::Button {
100 let mut btn = button::Button::default().with_label(caption);
101 btn.set_color(enums::Color::from_rgb(225, 225, 225));
102 btn
103}More examples
17fn main() {
18 let app = app::App::default();
19
20 // Act as the application in the taskbar (scroll to event handling)
21 let mut dock_win = window::Window::default()
22 .with_size(1, 1) // So we can place it at the center of the screen (needs a size >0 to be centered)
23 .with_label("TestApplication")
24 .center_screen();
25 dock_win.size_range(0, 0, 0, 0);
26 dock_win.make_resizable(false);
27
28 dock_win.show();
29 dock_win.end();
30
31 let mut win = window::Window::default()
32 .with_size(900, 500)
33 .with_label("TestApplication")
34 .center_screen();
35 win.set_color(enums::Color::from_rgb(26, 25, 55));
36
37 let mut but = button::Button::default()
38 .with_label("Button")
39 .with_size(80, 80)
40 .center_of_parent();
41 but.set_frame(enums::FrameType::OFlatFrame);
42 but.set_color(enums::Color::Cyan);
43 but.clear_visible_focus();
44 but.set_callback(|_| println!("Clicked"));
45
46 win.show();
47 win.end();
48
49 let win_shape = prep_shape(win.w(), win.h());
50
51 // Called after showing window
52 win.set_shape(Some(win_shape));
53
54 win.handle({
55 let mut x = 0;
56 let mut y = 0;
57 let mut dock_win = dock_win.clone();
58 move |wself, event| match event {
59 enums::Event::Push => {
60 let coords = app::event_coords();
61 x = coords.0;
62 y = coords.1;
63
64 true
65 }
66 enums::Event::Drag => {
67 wself.set_pos(app::event_x_root() - x, app::event_y_root() - y);
68
69 // Changing dock window position so it's close enough to the center of the application (not "visible" to user)
70 dock_win.set_pos(wself.x() + (wself.w() / 2), wself.y() + (wself.w() / 2));
71
72 true
73 }
74 enums::Event::Close => {
75 app.quit();
76
77 true
78 }
79 enums::Event::Hide => {
80 app.quit();
81
82 true
83 }
84 _ => false,
85 }
86 });
87
88 // Make main window appear when "opened" via Alt+Tab or Taskbar
89 dock_win.handle({
90 let mut win = win.clone();
91 move |_wself, event| match event {
92 enums::Event::Focus => {
93 let win_shape = prep_shape(win.w(), win.h());
94
95 win.show();
96 win.set_shape(Some(win_shape));
97
98 true
99 }
100 enums::Event::Hide => {
101 win.hide();
102
103 true
104 }
105 enums::Event::Close => {
106 app.quit();
107
108 true
109 }
110 _ => false,
111 }
112 });
113
114 app.run().unwrap();
115}557fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
558 let sel_len = term.selection_text_len();
559 let sel = term.selection_text();
560
561 term.take_focus().unwrap();
562 term.reset_terminal();
563 // Test the Utf8Char primitive
564 let uc = Utf8Char::new(b'Q');
565 let uc1 = uc.text_utf8();
566 assert_eq!(&uc1, b"Q");
567 assert_eq!(&uc.attrib(), &Attrib::Normal);
568 assert_eq!(
569 &uc.charflags(),
570 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
571 );
572 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
573 assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
574
575 let ring_rows = term.ring_rows();
576
577 term.take_focus().unwrap();
578 term.clear_history();
579 assert_eq!(term.history_use(), 0);
580
581 // Subtract row numbers, modulo `rows`
582 fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
583 match a - b {
584 n if n < 0 => n + rows,
585 n => n,
586 }
587 }
588 // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
589 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
590 assert!(term.disp_srow() >= 0);
591 assert!(term.disp_erow() >= 0);
592 assert!(term.hist_srow() >= 0);
593 assert!(term.hist_erow() >= 0);
594 assert!(term.offset() >= 0);
595 assert!(term.disp_srow() <= ring_rows);
596 assert!(term.disp_erow() <= ring_rows);
597 assert!(term.hist_srow() <= ring_rows);
598 assert!(term.hist_erow() <= ring_rows);
599 assert!(term.offset() <= ring_rows);
600
601 assert_eq!(term.ring_srow(), 0);
602 assert_eq!(term.ring_erow(), ring_rows - 1);
603 assert_eq!(
604 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
605 term.display_rows()
606 );
607 assert_eq!(
608 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
609 term.history_rows()
610 );
611
612 assert_eq!(term.ring_erow(), term.ring_rows() - 1);
613 assert_eq!(term.ring_srow(), 0);
614
615 /// Local function to read back all rows from the display into a long string.
616 /// Does not include scrollback history.
617 /// Trims trailing blanks on each line
618 fn read_disp(term: &Terminal) -> String {
619 let rows = term.display_rows();
620 let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
621 for row in 0..rows {
622 let r = term.u8c_disp_row(row).trim();
623 // Iterate through a row, accumulating [u8]
624 for c in r.iter() {
625 // Note: Sometimes utf-8 length is > 1
626 text.extend_from_slice(c.text_utf8());
627 }
628 text.extend_from_slice(b"\n");
629 }
630 // Return the result as a string
631 std::str::from_utf8(&text).unwrap().to_string()
632 }
633
634 term.clear();
635 term.append("Top line ↑ (up-arrow)");
636 term.set_text_attrib(Attrib::Underline);
637 term.append(" ");
638 term.set_text_attrib(Attrib::Normal);
639 term.append(" \n");
640 let mut text_out = read_disp(term);
641 // Trim trailing empty lines
642 text_out = text_out.trim_end_matches(&"\n").to_string();
643 // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
644
645 assert_eq!(text_out, "Top line ↑ (up-arrow) ");
646 let r = term.u8c_disp_row(0);
647 assert_eq!(r.col(0).text_utf8(), b"T");
648 assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
649 assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
650 let r = term.u8c_disp_row(1);
651 assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
652 assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
653
654 // Clear the screen again, then append test text, then read it back and compare
655 let test_text = "The wind was a torrent of darkness among the gusty trees.
656The moon was a ghostly galleon tossed upon cloudy seas.
657The road was a ribbon of moonlight over the purple moor,
658And the highwayman came riding—
659 Riding—riding—
660The highwayman came riding, up to the old inn-door.";
661
662 term.clear_history();
663 term.clear();
664 let bg_save = term.text_bg_color();
665 let fg_save = term.text_fg_color();
666 term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
667 term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
668 term.append(test_text);
669 term.set_text_bg_color(bg_save);
670 term.set_text_fg_color(fg_save);
671
672 let mut text_out = read_disp(term);
673 // Trim trailing empty lines
674 text_out = text_out.trim_end_matches(&"\n").to_string();
675 assert_eq!(test_text, text_out);
676
677 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
678
679 assert_eq!(term.ring_srow(), 0);
680 assert_eq!(term.ring_erow(), ring_rows - 1);
681 assert_eq!(
682 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
683 term.display_rows()
684 );
685 assert_eq!(
686 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
687 term.history_rows()
688 );
689
690 term.append(&format!(
691 "\n\nScreen has {} rows of {} columns.\n",
692 term.display_rows(),
693 term.display_columns()
694 ));
695
696 term.append(&format!("Selection len: {sel_len}\nSelection: '{sel}'\n"));
697}
698
699//--------------------------------------------------------------------------------------
700/// Yet another set of tests for misc cursor functions and other stuff
701/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
702fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
703 term.take_focus().unwrap();
704
705 // Test the attr_fg_color and attr_bg_color methods.
706 // Put a single character 'A' into the buffer and check it
707 term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
708 term.set_text_bg_color(Color::TransparentBg);
709 term.set_text_fg_color(Color::XtermWhite);
710 term.append("A");
711 let r = &term.u8c_disp_row(0);
712 let uc = r.col(0);
713 assert_eq!(uc.text_utf8(), b"A");
714 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
715 assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
716 assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
717 assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
718 assert_eq!(&uc.attrib(), &Attrib::Normal);
719
720 // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
721 term.clear();
722 term.set_text_fg_color_xterm(XtermColor::White);
723 term.set_text_bg_color_xterm(XtermColor::Black);
724 assert_eq!(term.text_attrib(), Attrib::Normal);
725
726 assert!(term.ansi());
727 term.append("B\x1b[32mC\x1b[1mD\n");
728
729 let r = &term.u8c_disp_row(0);
730 let uc = r.col(0);
731 assert_eq!(uc.text_utf8(), b"B");
732 assert!(uc.is_char(b'B'));
733 assert!(!uc.is_char(b'A'));
734 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
735 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
736 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
737 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
738 assert_eq!(
739 &uc.charflags(),
740 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
741 );
742
743 let uc = r.col(1);
744 assert_eq!(uc.text_utf8(), b"C");
745 assert!(uc.is_char(b'C'));
746 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
747 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
748 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
749 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
750 assert_eq!(
751 &uc.charflags(),
752 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
753 );
754
755 let uc = r.col(2);
756 assert_eq!(uc.text_utf8(), b"D");
757 assert!(uc.is_char(b'D'));
758 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
759 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
760 assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
761 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
762 assert_eq!(
763 &uc.attr_fgcolor(Some(term)),
764 &Color::from_rgb(0x20, 0xf0, 0x20)
765 );
766 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
767 assert_eq!(
768 &uc.charflags(),
769 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
770 );
771
772 // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
773 term.clear();
774 term.set_text_fg_color_xterm(XtermColor::White);
775 term.set_text_bg_color_xterm(XtermColor::Black);
776 term.set_text_attrib(Attrib::Normal);
777 assert_eq!(term.text_attrib(), Attrib::Normal);
778
779 assert!(term.ansi());
780 term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
781
782 let r = &term.u8c_disp_row(0);
783 let uc = r.col(0);
784 assert_eq!(uc.text_utf8(), b"B");
785 assert!(uc.is_char(b'B'));
786 assert!(!uc.is_char(b'A'));
787 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
788 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
789 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
790 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
791 assert_eq!(
792 &uc.charflags(),
793 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
794 );
795
796 let uc = r.col(1);
797 assert_eq!(uc.text_utf8(), b"C");
798 assert!(uc.is_char(b'C'));
799 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
800 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
801 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
802 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
803 assert_eq!(
804 &uc.charflags(),
805 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
806 );
807
808 let uc = r.col(2);
809 assert_eq!(uc.text_utf8(), b"D");
810 assert!(uc.is_char(b'D'));
811 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
812 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
813 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
814 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
815 assert_eq!(
816 &uc.charflags(),
817 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
818 );
819
820 let uc = r.col(3);
821 assert_eq!(uc.text_utf8(), b"E");
822 assert!(uc.is_char(b'E'));
823 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
824 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
825 assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
826 assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
827 assert_eq!(
828 &uc.charflags(),
829 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
830 );
831
832 // Test some miscellaneous Utf8 constants
833 assert_eq!(uc.length(), 1);
834 assert_eq!(uc.max_utf8(), 4);
835 assert_eq!(uc.pwidth(), 9.0);
836 assert_eq!(uc.pwidth_int(), 9);
837
838 term.set_text_fg_color_xterm(XtermColor::White);
839 term.set_text_bg_color_xterm(XtermColor::Black);
840 term.clear();
841 term.set_text_attrib(Attrib::Normal);
842
843 // Mouse selection functions
844 term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
845 term.clear_mouse_selection();
846 assert_eq!(term.get_selection(), None);
847
848 // Play with cursor position
849 term.append("0123456789\n"); // Set up test pattern
850 term.append("ABCDEFGHIJ\n");
851 term.append("abcdefghij\n");
852
853 term.set_cursor_row(1);
854 assert_eq!(term.cursor_row(), 1);
855 term.set_cursor_col(1);
856 assert_eq!(term.cursor_col(), 1);
857 assert_eq!(term.u8c_cursor().text_utf8(), b"1");
858
859 term.append("----"); // Overwrites text at cursor and moves cursor forward
860 assert_eq!(term.cursor_row(), 1);
861 assert_eq!(term.cursor_col(), 5);
862 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
863 term.set_cursor_col(1);
864 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
865
866 term.cursor_up(1, false);
867 assert_eq!(term.cursor_row(), 0);
868 assert_eq!(term.cursor_col(), 1);
869 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
870
871 // Hit top of screen, so nothing happens
872 term.cursor_up(1, false);
873 assert_eq!(term.cursor_row(), 0);
874 assert_eq!(term.cursor_col(), 1);
875 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
876
877 // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
878 term.cursor_up(1, true);
879 assert_eq!(term.cursor_row(), 0);
880 assert_eq!(term.cursor_col(), 1);
881 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
882
883 // Go back down to the overwritten text
884 term.cursor_down(2, false);
885 assert_eq!(term.cursor_row(), 2);
886 assert_eq!(term.cursor_col(), 1);
887 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
888
889 // Go right past the overwritten text
890 term.cursor_right(4, false);
891 assert_eq!(term.cursor_row(), 2);
892 assert_eq!(term.cursor_col(), 5);
893 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
894
895 // Go left to the end of the overwritten text
896 term.cursor_left(1);
897 assert_eq!(term.cursor_row(), 2);
898 assert_eq!(term.cursor_col(), 4);
899 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
900
901 // Scroll back down, removing the blank line at the top.
902 // Cursor stays in place, the text moves under it.
903 term.scroll(1);
904 assert_eq!(term.cursor_row(), 2);
905 assert_eq!(term.cursor_col(), 4);
906 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
907
908 // Clear from here to end-of-line
909 term.clear_eol();
910 assert_eq!(term.cursor_row(), 2);
911 assert_eq!(term.cursor_col(), 4);
912 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
913
914 // Now clear from here to start-of-line. Cursor does not move.
915 term.clear_sol();
916 assert_eq!(term.cursor_row(), 2);
917 assert_eq!(term.cursor_col(), 4);
918 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
919 term.cursor_left(1);
920 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
921 term.set_cursor_col(0);
922 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
923
924 // Clear some lines
925 term.clear_line(1);
926 assert_eq!(term.cursor_row(), 2);
927 assert_eq!(term.cursor_col(), 0);
928 term.set_cursor_row(1);
929 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930 term.set_cursor_row(3);
931 term.clear_cur_line();
932 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
933 assert_eq!(term.cursor_row(), 3);
934 assert_eq!(term.cursor_col(), 0);
935
936 term.append("Two lines above are intentionally left blank.\n");
937 assert_eq!(term.cursor_row(), 4);
938 assert_eq!(term.cursor_col(), 0);
939
940 // Set up the test pattern again, then play with insert/delete
941 term.append("0123456789\n");
942 term.append("ABCDEFGHIJ\n");
943 term.append("abcdefghij\n");
944 assert_eq!(term.cursor_row(), 7);
945
946 term.set_cursor_row(4);
947 term.set_cursor_col(4);
948 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
949
950 term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
951 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
952 term.cursor_right(5, false);
953 assert_eq!(term.cursor_col(), 9);
954 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
955
956 // Insert two blank rows above cursor. Cursor stays put.
957 term.insert_rows(2);
958 assert_eq!(term.cursor_row(), 4);
959 assert_eq!(term.cursor_col(), 9);
960 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
961 term.cursor_down(2, false); // Go down to find our text again
962 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
963
964 // Go back to the beginning of the inserted 'x' characters and delete them.
965 term.cursor_left(5);
966 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
967 term.delete_cur_chars(5);
968 assert_eq!(term.cursor_row(), 6);
969 assert_eq!(term.cursor_col(), 4);
970 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
971
972 term.delete_chars(7, 2, 2); // Delete "CD" from the next row
973 term.cursor_down(1, false);
974 term.cursor_left(2);
975 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
976
977 term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
978 assert_eq!(term.u8c_cursor().text_utf8(), b"c");
979 term.cursor_up(1, false);
980 term.delete_rows(2); // Delete remains of test pattern
981
982 term.set_text_attrib(Attrib::Bold);
983 term.insert_char_eol('-', 3, 15, 20);
984 term.set_cursor_row(3);
985 term.set_cursor_col(15);
986 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
987 assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
988
989 term.set_text_attrib(Attrib::Italic);
990 term.append(" and all lines below");
991 term.set_text_attrib(Attrib::Normal);
992 term.cursor_down(1, false);
993
994 let mut hsb = term.hscrollbar();
995 let mut sb = term.scrollbar();
996 hsb.set_value(100.0);
997 assert_eq!(hsb.value(), 100.0);
998 sb.set_value(50.0);
999 assert_eq!(sb.value(), 50.0);
1000 hsb.set_value(0.0);
1001 assert_eq!(hsb.value(), 0.0);
1002 sb.set_value(0.0);
1003 assert_eq!(sb.value(), 0.0);
1004}Sourcepub fn from_rgba_tuple(tup: (u8, u8, u8, u8)) -> Color
pub fn from_rgba_tuple(tup: (u8, u8, u8, u8)) -> Color
Create color from RGBA using alpha compositing. Works for non-group types.
Sourcepub const fn from_u32(val: u32) -> Color
pub const fn from_u32(val: u32) -> Color
Returns a color from hex or decimal
Examples found in repository?
More examples
64fn main() {
65 let app = app::App::default();
66 app::background(255, 255, 255);
67 let mut win = window::Window::default().with_size(400, 300);
68 let mut dial = MyDial::new(100, 100, 200, 200, "CPU Load %");
69 dial.set_label_size(22);
70 dial.set_label_color(Color::from_u32(0x797979));
71 win.end();
72 win.show();
73
74 // get the cpu load value from somewhere, then call dial.set_value() in a callback or event loop
75 dial.set_value(10);
76
77 app.run().unwrap();
78}63fn draw_data(txt: &str, x: i32, y: i32, w: i32, h: i32, selected: bool) {
64 draw::push_clip(x, y, w, h);
65 if selected {
66 draw::set_draw_color(enums::Color::from_u32(0x00D3_D3D3));
67 } else {
68 draw::set_draw_color(enums::Color::White);
69 }
70 draw::draw_rectf(x, y, w, h);
71 draw::set_draw_color(enums::Color::Gray0);
72 draw::set_font(enums::Font::Helvetica, 14);
73 draw::draw_text2(txt, x, y, w, h, enums::Align::Center);
74 draw::draw_rect(x, y, w, h);
75 draw::pop_clip();
76}70 pub fn new(buf: text::TextBuffer) -> Self {
71 let mut editor = text::TextEditor::new(5, 35, 790, 560, "");
72 editor.set_buffer(Some(buf));
73
74 #[cfg(target_os = "macos")]
75 editor.resize(5, 5, 790, 590);
76
77 editor.set_scrollbar_size(15);
78 editor.set_text_font(Font::Courier);
79 editor.set_linenumber_width(32);
80 editor.set_linenumber_fgcolor(Color::from_u32(0x008b_8386));
81 editor.set_trigger(CallbackTrigger::Changed);
82
83 Self { editor }
84 }129 fn draw_data(txt: &str, x: i32, y: i32, w: i32, h: i32, selected: bool) {
130 draw::push_clip(x, y, w, h);
131 if selected {
132 draw::set_draw_color(enums::Color::from_u32(0x00D3_D3D3));
133 } else {
134 draw::set_draw_color(enums::Color::White);
135 }
136 draw::draw_rectf(x, y, w, h);
137 draw::set_draw_color(enums::Color::Gray0);
138 draw::set_font(enums::Font::Helvetica, 14);
139 draw::draw_text2(txt, x, y, w, h, enums::Align::Center);
140 draw::draw_rect(x, y, w, h);
141 draw::pop_clip();
142 }9 pub fn new(w: i32, h: i32) -> MyButton {
10 let mut grp = group::Group::new(0, 0, w, h, None);
11 grp.set_frame(enums::FrameType::RFlatBox);
12 grp.set_color(enums::Color::from_u32(0x01579b));
13 grp.set_align(enums::Align::Center);
14 let mut btn = button::Button::new(grp.x() + 420, grp.y() + 35, 30, 25, "@1+");
15 btn.set_frame(enums::FrameType::OFlatFrame);
16 btn.set_color(enums::Color::from_u32(0xf49da9));
17 btn.set_callback(move |b| b.parent().unwrap().hide());
18 grp.end();
19 grp.handle(|g, ev| match ev {
20 enums::Event::Push => {
21 g.do_callback();
22 true
23 }
24 _ => false,
25 });
26 MyButton { grp }
27 }Sourcepub const fn from_hex(val: u32) -> Color
pub const fn from_hex(val: u32) -> Color
Returns a color from hex or decimal
Examples found in repository?
More examples
37 pub fn new(title: &'static str) -> MyButton {
38 let mut b = Button::new(0, 0, 100, 0, title);
39 b.set_label_size(24);
40 b.set_frame(FrameType::FlatBox);
41 match title {
42 "CE" => {
43 b.set_color(Color::from_hex(0xd50000));
44 b.set_shortcut(Shortcut::None | Key::Delete);
45 }
46 "x" | "/" | "+" | "-" | "=" | "C" | "@<-" => {
47 b.set_color(Color::from_hex(0xffee58));
48 b.set_label_color(Color::Black);
49 let shortcut = if title == "x" {
50 '*'
51 } else {
52 title.chars().next().unwrap()
53 };
54 b.set_shortcut(Shortcut::None | shortcut);
55 if shortcut == '@' {
56 b.set_shortcut(Shortcut::None | Key::BackSpace);
57 }
58 if shortcut == '=' {
59 b.set_shortcut(Shortcut::None | Key::Enter);
60 }
61 }
62 _ => {
63 if title == "0" {
64 b.resize(0, 0, 100 * 2, 0);
65 }
66 b.set_label_color(Color::White);
67 b.set_selection_color(Color::from_hex(0x1b1b1b));
68 b.set_shortcut(Shortcut::None | title.chars().next().unwrap());
69 b.handle(move |b, ev| match ev {
70 Event::Enter => {
71 b.set_color(Color::from_hex(0x2b2b2b));
72 b.redraw();
73 true
74 }
75 Event::Leave => {
76 b.set_color(Color::from_hex(0x424242));
77 b.redraw();
78 true
79 }
80 _ => false,
81 });
82 }
83 }
84 Self { b }
85 }
86}
87
88impl Deref for MyButton {
89 type Target = Button;
90
91 fn deref(&self) -> &Self::Target {
92 &self.b
93 }
94}
95
96impl DerefMut for MyButton {
97 fn deref_mut(&mut self) -> &mut Self::Target {
98 &mut self.b
99 }
100}
101
102fn main() {
103 let app = app::App::default();
104 app::set_visible_focus(false);
105 app::background(0x42, 0x42, 0x42);
106
107 let win_w = 400;
108 let win_h = 500;
109 let but_row = 160;
110
111 let mut operation = Ops::None;
112 let mut txt = String::from("0");
113 let mut old_val = String::from("0");
114 let mut new_val: String;
115
116 let mut wind = Window::default()
117 .with_label("FLTK Calc")
118 .with_size(win_w, win_h)
119 .center_screen();
120
121 let mut out = Frame::new(0, 0, win_w, 160, "").with_align(Align::Right | Align::Inside);
122 out.set_color(Color::from_hex(0x1b1b1b));
123 out.set_frame(FrameType::FlatBox);
124 out.set_label_color(Color::White);
125 out.set_label_size(36);
126 out.set_label("0");
127
128 let vpack = Pack::new(0, but_row, win_w, win_h - 170, "");
129
130 let mut hpack = Pack::new(0, 0, win_w, 68, "");
131 let but_ce = MyButton::new("CE");
132 let but_c = MyButton::new("C");
133 let but_back = MyButton::new("@<-");
134 let but_div = MyButton::new("/");
135 hpack.end();
136 hpack.set_type(PackType::Horizontal);
137
138 let mut hpack = Pack::new(0, 0, win_w, 68, "");
139 let mut but7 = MyButton::new("7");
140 let mut but8 = MyButton::new("8");
141 let mut but9 = MyButton::new("9");
142 let but_mul = MyButton::new("x");
143 hpack.end();
144 hpack.set_type(PackType::Horizontal);
145
146 let mut hpack = Pack::new(0, 0, win_w, 68, "");
147 let mut but4 = MyButton::new("4");
148 let mut but5 = MyButton::new("5");
149 let mut but6 = MyButton::new("6");
150 let but_sub = MyButton::new("-");
151 hpack.end();
152 hpack.set_type(PackType::Horizontal);
153
154 let mut hpack = Pack::new(0, 0, win_w, 68, "");
155 let mut but1 = MyButton::new("1");
156 let mut but2 = MyButton::new("2");
157 let mut but3 = MyButton::new("3");
158 let but_add = MyButton::new("+");
159 hpack.end();
160 hpack.set_type(PackType::Horizontal);
161
162 let mut hpack = Pack::new(0, 0, win_w, 68, "");
163 let mut but_dot = MyButton::new(".");
164 let mut but0 = MyButton::new("0");
165 let but_eq = MyButton::new("=");
166 hpack.end();
167 hpack.set_type(PackType::Horizontal);
168
169 vpack.end();
170
171 wind.make_resizable(false);
172 wind.end();
173 wind.show();
174
175 app::set_focus(&*but1);
176 app::get_system_colors();
177
178 let but_vec = vec![
179 &mut but1, &mut but2, &mut but3, &mut but4, &mut but5, &mut but6, &mut but7, &mut but8,
180 &mut but9, &mut but0,
181 ];
182
183 let but_op_vec = vec![
184 but_add, but_sub, but_mul, but_div, but_c, but_ce, but_back, but_eq,
185 ];
186
187 let (s, r) = app::channel::<Message>();
188
189 for but in but_vec {
190 let label = but.label();
191 but.emit(s, Message::Number(label.parse().unwrap()));
192 }
193
194 for mut but in but_op_vec {
195 let op = match but.label().as_str() {
196 "+" => Ops::Add,
197 "-" => Ops::Sub,
198 "x" => Ops::Mul,
199 "/" => Ops::Div,
200 "=" => Ops::Eq,
201 "CE" => Ops::CE,
202 "C" => Ops::C,
203 "@<-" => Ops::Back,
204 _ => Ops::None,
205 };
206 but.emit(s, Message::Op(op));
207 }
208
209 but_dot.emit(s, Message::Dot);
210
211 while app.wait() {
212 if let Some(val) = r.recv() {
213 match val {
214 Message::Number(num) => {
215 if out.label() == "0" {
216 txt.clear();
217 }
218 txt.push_str(&num.to_string());
219 out.set_label(txt.as_str());
220 }
221 Message::Dot => {
222 if operation == Ops::Eq {
223 txt.clear();
224 operation = Ops::None;
225 out.set_label("0.");
226 txt.push_str("0.");
227 }
228 if !txt.contains('.') {
229 txt.push('.');
230 out.set_label(txt.as_str());
231 }
232 }
233 Message::Op(op) => match op {
234 Ops::Add | Ops::Sub | Ops::Div | Ops::Mul => {
235 old_val.clear();
236 old_val.push_str(&out.label());
237 operation = op;
238 out.set_label("0");
239 }
240 Ops::Back => {
241 let val = out.label();
242 txt.pop();
243 if val.len() > 1 {
244 out.set_label(txt.as_str());
245 } else {
246 out.set_label("0");
247 }
248 }
249 Ops::CE => {
250 txt.clear();
251 old_val.clear();
252 txt.push('0');
253 out.set_label(txt.as_str());
254 }
255 Ops::C => {
256 txt.clear();
257 txt.push('0');
258 out.set_label(txt.as_str());
259 }
260 Ops::Eq => {
261 new_val = out.label();
262 let old: f64 = old_val.parse().unwrap();
263 let new: f64 = new_val.parse().unwrap();
264 let val = match operation {
265 Ops::Div => old / new,
266 Ops::Mul => old * new,
267 Ops::Add => old + new,
268 Ops::Sub => old - new,
269 _ => new,
270 };
271 operation = Ops::None;
272 txt = String::from("0");
273 out.set_label(&val.to_string());
274 }
275 _ => (),
276 },
277 }
278 }
279 }
280}20fn main() {
21 let app = fltk::app::App::default();
22
23 // Set panic handler for main thread (will become UI thread)
24 std::panic::set_hook(Box::new({
25 |e| {
26 eprintln!("!!!!PANIC!!!!{:#?}", e);
27 error_box(e.to_string()); // Only works from the UI thread
28 std::process::exit(2);
29 }
30 }));
31
32 let mut main_win = Window::new(
33 2285,
34 180,
35 WIN_WIDTH,
36 WIN_HEIGHT,
37 "FLTK/Terminal Rust wrapper test",
38 );
39 main_win.set_type(WindowType::Double);
40 main_win.make_resizable(true);
41
42 let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44 let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45 term.set_label("term");
46 main_win.resizable(&term);
47 term.set_label_type(LabelType::None);
48
49 let idx = menu_bar.add_choice("Test&1");
50 menu_bar.at(idx).unwrap().set_callback({
51 let mut term1 = term.clone();
52 move |c| mb_test1_cb(c, &mut term1)
53 });
54 menu_bar
55 .at(idx)
56 .unwrap()
57 .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59 let idx = menu_bar.add_choice("Test&2");
60 menu_bar.at(idx).unwrap().set_callback({
61 let mut term1 = term.clone();
62 move |c| mb_test2_cb(c, &mut term1)
63 });
64 menu_bar
65 .at(idx)
66 .unwrap()
67 .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69 let idx = menu_bar.add_choice("Test&3");
70 menu_bar.at(idx).unwrap().set_callback({
71 let mut term1 = term.clone();
72 move |c| mb_test3_cb(c, &mut term1)
73 });
74 menu_bar
75 .at(idx)
76 .unwrap()
77 .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79 let idx = menu_bar.add_choice("Test&4");
80 menu_bar.at(idx).unwrap().set_callback({
81 let mut term1 = term.clone();
82 move |c| mb_test4_cb(c, &mut term1)
83 });
84 menu_bar
85 .at(idx)
86 .unwrap()
87 .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89 let idx = menu_bar.add_choice("Test&5");
90 menu_bar.at(idx).unwrap().set_callback({
91 let mut term1 = term.clone();
92 move |c| mb_test5_cb(c, &mut term1)
93 });
94 menu_bar
95 .at(idx)
96 .unwrap()
97 .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99 menu_bar.end();
100
101 main_win.end();
102 main_win.show();
103
104 // Worker thread that drives the startup tests
105 let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106 let mut term = term.clone();
107 move || {
108 println!("Startup tests\n");
109 term.append("Startup tests\n\n");
110 term.append("<tmp>\n"); // This line will be overwritten later
111
112 term.cursor_up(2, false);
113 assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114 assert_eq!(term.text(true), "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
115
116 // Testing ansi() and set_ansi() methods
117 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
118 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
119 term.set_ansi(false);
120 assert!(!term.ansi());
121 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
122 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
123 term.append_u8(b"Appending u8 array\n");
124 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
125 term.set_ansi(true); // Restore ANSI state
126
127 // Play with the horizontal scrollbar
128 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
129 term.set_hscrollbar_style(ScrollbarStyle::ON);
130 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
131
132 // Test show_unknown() as incidental part of testing append methods
133 term.set_show_unknown(true);
134 assert!(term.show_unknown());
135 term.append_ascii(
136 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
137 );
138 term.set_show_unknown(false);
139 assert!(!term.show_unknown());
140
141 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
142 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
143
144 let r = term.cursor_row();
145 assert_eq!(term.cursor_col(), 0);
146 term.append(&format!("Testing cursor row/col {r}"));
147 assert_eq!(term.cursor_col(), 24);
148 assert_eq!(term.cursor_row(), r);
149
150 // Test cursor color methods
151 assert_eq!(
152 term.cursor_bg_color(),
153 Color::XtermGreen,
154 "Default cursor bg at startup"
155 );
156 assert_eq!(
157 term.cursor_fg_color(),
158 Color::from_hex(0xff_ff_f0),
159 "Default cursor fg at startup"
160 );
161 term.set_cursor_bg_color(Color::Red);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
164 term.set_cursor_fg_color(Color::Blue);
165 assert_eq!(term.cursor_bg_color(), Color::Red);
166 assert_eq!(term.cursor_fg_color(), Color::Blue);
167 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
168 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
169 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
170 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
171
172 // The default display_rows() will derive from the window size
173 let dr = term.display_rows();
174 let height = term.height();
175 assert_eq!(height, term.h());
176 assert!(dr > 20, "Default display_rows at startup");
177 term.resize(term.x(), term.y(), term.w(), height * 2);
178 assert_eq!(term.h(), height * 2);
179 assert_eq!(height * 2, term.h());
180 assert!(term.display_rows() > dr);
181 term.resize(term.x(), term.y(), term.w(), height);
182
183 // The default display_columns() will derive from the window size
184 let dc = term.display_columns();
185 assert!(dc > 80, "Default display_rows at startup");
186 term.set_display_columns(200);
187 assert_eq!(term.display_columns(), 200);
188 term.append("\n 1 2 3 4 5 6 7 8 9");
189 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
190 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
191 term.set_display_columns(90);
192 assert_eq!(term.display_columns(), 90);
193 term.set_display_columns(dc); // Set back to default
194 assert_eq!(term.display_columns(), dc);
195
196 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
197 term.set_history_rows(50);
198 assert_eq!(term.history_rows(), 50);
199 term.set_history_rows(100); // Set back to default
200 assert_eq!(term.history_rows(), 100);
201
202 let hu = term.history_use();
203 term.append(&format!(
204 "history_use = {hu} (it's not clear what this means)\n"
205 ));
206 // assert_eq!(term.history_use(), hu+1);
207
208 term.append(&format!(
209 "margins = b:{} l:{} r:{} t{}\n",
210 term.margin_bottom(),
211 term.margin_left(),
212 term.margin_right(),
213 term.margin_top()
214 ));
215 assert_eq!(term.margin_bottom(), 3);
216 assert_eq!(term.margin_left(), 3);
217 assert_eq!(term.margin_right(), 3);
218 assert_eq!(term.margin_top(), 3);
219
220 term.set_margin_bottom(5);
221 term.set_margin_left(10);
222 term.set_margin_right(15);
223 term.set_margin_top(20);
224 assert_eq!(term.margin_bottom(), 5);
225 assert_eq!(term.margin_left(), 10);
226 assert_eq!(term.margin_right(), 15);
227 assert_eq!(term.margin_top(), 20);
228
229 term.append("Single character: '");
230 term.print_char('X');
231 term.append("', single UTF-8 character: '");
232 term.print_char_utf8('↑');
233 term.append("'\n");
234
235 let rr = term.redraw_rate();
236 assert_eq!(rr, 0.1, "Default redraw rate at startup");
237 term.append(&format!("Redraw rate {rr}\n"));
238 term.set_redraw_rate(1.0);
239 assert_eq!(term.redraw_rate(), 1.0);
240 term.set_redraw_rate(rr);
241 assert_eq!(term.redraw_rate(), rr);
242
243 let rs = term.redraw_style();
244 term.append(&format!("Redraw style {rs:?}\n"));
245 assert_eq!(
246 rs,
247 RedrawStyle::RateLimited,
248 "Default redraw style at startup"
249 );
250 term.set_redraw_style(RedrawStyle::NoRedraw);
251 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
252 term.set_redraw_style(rs);
253 assert_eq!(term.redraw_style(), rs);
254
255 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
256 assert_eq!(
257 RedrawStyle::NoRedraw.bits(),
258 0x0000,
259 "RedrawStyle enum values have been reassigned"
260 );
261 assert_eq!(
262 RedrawStyle::RateLimited.bits(),
263 0x0001,
264 "RedrawStyle enum values have been reassigned"
265 );
266 assert_eq!(
267 RedrawStyle::PerWrite.bits(),
268 0x0002,
269 "RedrawStyle enum values have been reassigned"
270 );
271
272 let sb = term.scrollbar();
273 let hsb = term.hscrollbar();
274 // Both vertical and horizontal scrollbars are at zero
275 assert_eq!(sb.value(), 0.0);
276 assert_eq!(hsb.value(), 0.0);
277 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
278
279 term.append(&format!(
280 "Scrollbar actual size {}\n",
281 term.scrollbar_actual_size()
282 ));
283 assert_eq!(term.scrollbar_actual_size(), 16);
284 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
285 assert_eq!(
286 term.scrollbar_size(),
287 0,
288 "Default scrollbar size at startup"
289 );
290 term.set_scrollbar_size(40);
291 assert_eq!(term.scrollbar_size(), 40);
292 assert_eq!(term.scrollbar_actual_size(), 40);
293 term.append(&format!(
294 "Scrollbar actual size {}\n",
295 term.scrollbar_actual_size()
296 ));
297 term.set_scrollbar_size(0); // Restore default
298 assert_eq!(term.scrollbar_size(), 0);
299 assert_eq!(term.scrollbar_actual_size(), 16);
300
301 let sfc = term.selection_fg_color();
302 let sbc = term.selection_bg_color();
303 assert_eq!(sfc, Color::Black);
304 assert_eq!(sbc, Color::White);
305 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
306 term.set_selection_fg_color(Color::Green);
307 term.set_selection_bg_color(Color::DarkBlue);
308 assert_eq!(term.selection_fg_color(), Color::Green);
309 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
310 term.set_selection_fg_color(sfc);
311 term.set_selection_bg_color(sbc);
312 assert_eq!(term.selection_fg_color(), Color::Black);
313 assert_eq!(term.selection_bg_color(), Color::White);
314
315 let tfcd = term.text_fg_color_default();
316 let tbcd = term.text_bg_color_default();
317 assert_eq!(tfcd, Color::XtermWhite);
318 assert_eq!(tbcd, Color::TransparentBg);
319 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
320 term.set_text_fg_color_default(Color::Green);
321 term.set_text_bg_color_default(Color::DarkBlue);
322 assert_eq!(term.text_fg_color_default(), Color::Green);
323 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
324 term.set_text_fg_color_default(tfcd);
325 term.set_text_bg_color_default(tbcd);
326 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
327 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
328
329 let tfc = term.text_fg_color();
330 let tbc = term.text_bg_color();
331 assert_eq!(tfc, Color::XtermWhite);
332 assert_eq!(tbc, Color::TransparentBg);
333 term.append(&format!("Text colors: {sfc} {sbc}\n"));
334 term.set_text_fg_color(Color::Green);
335 term.set_text_bg_color(Color::DarkBlue);
336 assert_eq!(term.text_fg_color(), Color::Green);
337 assert_eq!(term.text_bg_color(), Color::DarkBlue);
338 term.set_text_fg_color(tfc);
339 term.set_text_bg_color(tbc);
340 assert_eq!(term.text_fg_color(), Color::XtermWhite);
341 assert_eq!(term.text_bg_color(), Color::TransparentBg);
342
343 let tf = term.text_font();
344 term.append(&format!("Text font: {tf:?}\n"));
345 assert_eq!(tf, Font::Courier);
346 term.set_text_font(Font::Screen);
347 assert_eq!(term.text_font(), Font::Screen);
348 term.set_text_font(tf);
349 assert_eq!(term.text_font(), Font::Courier);
350
351 let ts = term.text_size();
352 let r = term.h_to_row(100);
353 let c = term.w_to_col(100);
354 term.append(&format!(
355 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
356 ));
357 assert_eq!(ts, 14);
358 term.set_text_size(30);
359 assert_eq!(term.text_size(), 30);
360 term.append(&format!(
361 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
362 term.text_size(),
363 term.h_to_row(100),
364 term.w_to_col(100)
365 ));
366 term.set_text_size(ts);
367 assert_eq!(term.text_size(), ts);
368 term.append(&format!(
369 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
370 term.text_size(),
371 term.h_to_row(100),
372 term.w_to_col(100)
373 ));
374
375 // Keyboard handler
376 term.handle({
377 move |term, e| {
378 match e {
379 fltk::enums::Event::KeyDown
380 if fltk::app::event_key() == fltk::enums::Key::Escape =>
381 {
382 // false to let FLTK handle ESC. true to hide ESC
383 false
384 }
385
386 fltk::enums::Event::KeyDown
387 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
388 {
389 // We handle control keystroke
390 let k = fltk::app::event_text();
391 term.append_utf8(&k);
392 true
393 }
394
395 fltk::enums::Event::KeyDown
396 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
397 {
398 // We handle normal printable keystroke
399 let k = fltk::app::event_text();
400 term.take_focus().unwrap();
401 term.append(&k);
402 true
403 }
404
405 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
406 // We can do this, or else ignore them (return false)
407 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
408 // term.redraw();
409 // true
410 // }
411 _ => false, // Let FLTK handle everything else
412 }
413 }
414 });
415
416 let attr_save = term.text_attrib();
417 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
418 term.append("\nStartup tests complete. Keyboard is live.\n");
419 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
420 term.set_text_attrib(attr_save);
421 assert_eq!(term.text_attrib(), attr_save);
422 term.redraw();
423 }
424 });
425
426 app.run().unwrap();
427}
428//--------------------------------------------------------------------------------------
429/// More tests that run when the menu bar Test1 is clicked
430fn mb_test1_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
431 term.take_focus().unwrap();
432 term.reset_terminal();
433 term.append("0123456789 0\n");
434 term.append("0123456789 1\n");
435 term.append("0123456789 2\n");
436 term.append("0123456789 3\n");
437 term.append("0123456789 4\n");
438 term.append("0123456789 5\n");
439 term.append("0123456789 6\n");
440 term.append("0123456789 7\n");
441 term.append("0123456789 8\n");
442 term.append("0123456789 9\n");
443 term.append("------------\n");
444
445 term.set_text_fg_color(Color::Green);
446 term.plot_char('A', 0, 0);
447 term.plot_char('B', 1, 1);
448 term.plot_char('C', 2, 2);
449 term.plot_char('D', 3, 3);
450 term.plot_char('E', 4, 4);
451 term.plot_char('F', 5, 5);
452 term.set_text_fg_color(Color::XtermWhite);
453
454 assert_eq!(term.cursor_row(), 11);
455 assert_eq!(term.cursor_col(), 0);
456
457 term.set_text_bg_color(Color::DarkBlue);
458 term.plot_char_utf8('b', 8, 1);
459 term.plot_char_utf8('↑', 9, 1);
460 term.plot_char_utf8('c', 8, 2);
461 term.plot_char_utf8('↑', 9, 2);
462 term.plot_char_utf8('d', 8, 3);
463 term.plot_char_utf8('↑', 9, 3);
464 term.plot_char_utf8('e', 8, 4);
465 term.plot_char_utf8('↑', 9, 4);
466 term.plot_char_utf8('f', 8, 5);
467 term.plot_char_utf8('↑', 9, 5);
468 term.plot_char_utf8('g', 8, 6);
469 term.plot_char_utf8('↑', 9, 6);
470 term.set_text_bg_color(Color::TransparentBg);
471
472 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
473 term.append("Done!\n");
474 term.set_text_attrib(Attrib::Normal);
475}
476
477//--------------------------------------------------------------------------------------
478/// More tests that run when the menu bar button Test2 is clicked
479fn mb_test2_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
480 term.take_focus().unwrap();
481 term.reset_terminal();
482
483 for i in 0..50 {
484 term.append(&format!("{i}\n"));
485 }
486 assert_eq!(term.history_rows(), 100);
487
488 term.clear_history();
489 assert_eq!(term.history_use(), 0);
490
491 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
492 term.append("\nDone!\n");
493 term.set_text_attrib(Attrib::Normal);
494}
495
496//--------------------------------------------------------------------------------------
497/// Another set of tests that run when Test3 is clicked
498fn mb_test3_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
499 term.take_focus().unwrap();
500 term.reset_terminal();
501 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
502
503 assert_eq!(term.history_use(), 0);
504 term.clear();
505 assert_eq!(term.cursor_row(), 0);
506 assert_eq!(term.history_use(), term.display_rows()); // A screenful of lines added to history
507
508 term.append("Test\ntext\na\nb\nc\nd");
509 assert_eq!(term.cursor_row(), 5);
510 let hist = term.history_use();
511 term.clear_screen_home(false);
512 assert_eq!(term.cursor_row(), 0);
513 assert_eq!(term.history_use(), hist); // History not changed
514
515 term.append("Test\ntext\na\nb\nc\nd\ne");
516 assert_eq!(term.cursor_row(), 6);
517 term.clear_screen_home(true);
518 assert_eq!(term.cursor_row(), 0);
519
520 term.append("Test\ntext\na\nb\nc\n");
521 assert_eq!(term.cursor_row(), 5);
522 term.clear_to_color(Color::DarkBlue);
523 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
524 assert_eq!(term.text_bg_color(), Color::TransparentBg);
525 assert_eq!(term.cursor_row(), 0);
526
527 // Test cursor_home()
528 term.append("Test\n\n\n\n\n\n\n\n\n\n");
529 assert_eq!(term.cursor_row(), 10);
530 term.cursor_home();
531 assert_eq!(term.cursor_row(), 0);
532
533 // Test the widget color
534 assert_eq!(term.color(), Color::Black); // Default
535 term.set_color(Color::DarkGreen);
536 assert_eq!(term.color(), Color::DarkGreen);
537 term.set_color(Color::Black);
538 assert_eq!(term.color(), Color::Black);
539 term.append(
540 "This should be one line of white text on black, embedded into the top of a blue field.\n",
541 );
542
543 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF); // default
544 term.set_output_translate(OutFlags::OFF);
545 assert_eq!(term.output_translate(), OutFlags::OFF);
546 term.set_output_translate(OutFlags::LF_TO_CRLF); // restore default
547 assert_eq!(term.output_translate(), OutFlags::LF_TO_CRLF);
548
549 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
550 term.append("\nDone!\n");
551 term.set_text_attrib(Attrib::Normal);
552}
553
554//--------------------------------------------------------------------------------------
555/// Another set of tests for the ring-buffer access methods
556/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
557fn mb_test4_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
558 let sel_len = term.selection_text_len();
559 let sel = term.selection_text();
560
561 term.take_focus().unwrap();
562 term.reset_terminal();
563 // Test the Utf8Char primitive
564 let uc = Utf8Char::new(b'Q');
565 let uc1 = uc.text_utf8();
566 assert_eq!(&uc1, b"Q");
567 assert_eq!(&uc.attrib(), &Attrib::Normal);
568 assert_eq!(
569 &uc.charflags(),
570 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
571 );
572 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
573 assert_eq!(&uc.bgcolor(), &Color::TransparentBg);
574
575 let ring_rows = term.ring_rows();
576
577 term.take_focus().unwrap();
578 term.clear_history();
579 assert_eq!(term.history_use(), 0);
580
581 // Subtract row numbers, modulo `rows`
582 fn row_diff(rows: i32, a: i32, b: i32) -> i32 {
583 match a - b {
584 n if n < 0 => n + rows,
585 n => n,
586 }
587 }
588 // disp_srow is always 1 greater than hist_erow, modulo (ring_rows+1)
589 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
590 assert!(term.disp_srow() >= 0);
591 assert!(term.disp_erow() >= 0);
592 assert!(term.hist_srow() >= 0);
593 assert!(term.hist_erow() >= 0);
594 assert!(term.offset() >= 0);
595 assert!(term.disp_srow() <= ring_rows);
596 assert!(term.disp_erow() <= ring_rows);
597 assert!(term.hist_srow() <= ring_rows);
598 assert!(term.hist_erow() <= ring_rows);
599 assert!(term.offset() <= ring_rows);
600
601 assert_eq!(term.ring_srow(), 0);
602 assert_eq!(term.ring_erow(), ring_rows - 1);
603 assert_eq!(
604 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
605 term.display_rows()
606 );
607 assert_eq!(
608 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
609 term.history_rows()
610 );
611
612 assert_eq!(term.ring_erow(), term.ring_rows() - 1);
613 assert_eq!(term.ring_srow(), 0);
614
615 /// Local function to read back all rows from the display into a long string.
616 /// Does not include scrollback history.
617 /// Trims trailing blanks on each line
618 fn read_disp(term: &Terminal) -> String {
619 let rows = term.display_rows();
620 let mut text: Vec<u8> = Vec::with_capacity((rows * 64) as usize);
621 for row in 0..rows {
622 let r = term.u8c_disp_row(row).trim();
623 // Iterate through a row, accumulating [u8]
624 for c in r.iter() {
625 // Note: Sometimes utf-8 length is > 1
626 text.extend_from_slice(c.text_utf8());
627 }
628 text.extend_from_slice(b"\n");
629 }
630 // Return the result as a string
631 std::str::from_utf8(&text).unwrap().to_string()
632 }
633
634 term.clear();
635 term.append("Top line ↑ (up-arrow)");
636 term.set_text_attrib(Attrib::Underline);
637 term.append(" ");
638 term.set_text_attrib(Attrib::Normal);
639 term.append(" \n");
640 let mut text_out = read_disp(term);
641 // Trim trailing empty lines
642 text_out = text_out.trim_end_matches(&"\n").to_string();
643 // The two plain blanks at the end will be trimmed, the two underlined blanks will be retained.
644
645 assert_eq!(text_out, "Top line ↑ (up-arrow) ");
646 let r = term.u8c_disp_row(0);
647 assert_eq!(r.col(0).text_utf8(), b"T");
648 assert_eq!(r.col(10).text_utf8(), b"\xe2\x86\x91"); // UTF-8 up-arrow
649 assert_eq!(r.col(24).text_utf8(), b" "); // First blank after test text, NOT trimmed
650 let r = term.u8c_disp_row(1);
651 assert_eq!(r.col(0).text_utf8(), b" "); // Second row starts with blanks
652 assert_eq!(r.col(1).text_utf8(), b" "); // Second row is full of blanks
653
654 // Clear the screen again, then append test text, then read it back and compare
655 let test_text = "The wind was a torrent of darkness among the gusty trees.
656The moon was a ghostly galleon tossed upon cloudy seas.
657The road was a ribbon of moonlight over the purple moor,
658And the highwayman came riding—
659 Riding—riding—
660The highwayman came riding, up to the old inn-door.";
661
662 term.clear_history();
663 term.clear();
664 let bg_save = term.text_bg_color();
665 let fg_save = term.text_fg_color();
666 term.set_text_bg_color(Color::DarkBlue); // Set spooky colors
667 term.set_text_fg_color(Color::from_rgb(0x40, 0x40, 0xff));
668 term.append(test_text);
669 term.set_text_bg_color(bg_save);
670 term.set_text_fg_color(fg_save);
671
672 let mut text_out = read_disp(term);
673 // Trim trailing empty lines
674 text_out = text_out.trim_end_matches(&"\n").to_string();
675 assert_eq!(test_text, text_out);
676
677 assert_eq!(row_diff(ring_rows, term.disp_srow(), term.hist_erow()), 1);
678
679 assert_eq!(term.ring_srow(), 0);
680 assert_eq!(term.ring_erow(), ring_rows - 1);
681 assert_eq!(
682 row_diff(ring_rows, term.disp_erow(), term.disp_srow()) + 1,
683 term.display_rows()
684 );
685 assert_eq!(
686 row_diff(ring_rows, term.hist_erow(), term.hist_srow()) + 1,
687 term.history_rows()
688 );
689
690 term.append(&format!(
691 "\n\nScreen has {} rows of {} columns.\n",
692 term.display_rows(),
693 term.display_columns()
694 ));
695
696 term.append(&format!("Selection len: {sel_len}\nSelection: '{sel}'\n"));
697}
698
699//--------------------------------------------------------------------------------------
700/// Yet another set of tests for misc cursor functions and other stuff
701/// Note: these tests depend heavily on the low-level "protected" parts of the fltk library, which should be used with caution.
702fn mb_test5_cb(_choice: &mut fltk::menu::Choice, term: &mut Terminal) {
703 term.take_focus().unwrap();
704
705 // Test the attr_fg_color and attr_bg_color methods.
706 // Put a single character 'A' into the buffer and check it
707 term.clear(); // No reset_terminal(), just clear() to preserve the mouse selection for later
708 term.set_text_bg_color(Color::TransparentBg);
709 term.set_text_fg_color(Color::XtermWhite);
710 term.append("A");
711 let r = &term.u8c_disp_row(0);
712 let uc = r.col(0);
713 assert_eq!(uc.text_utf8(), b"A");
714 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
715 assert_eq!(&uc.attr_bgcolor(None), &Color::TransparentBg);
716 assert_eq!(&uc.attr_bgcolor(Some(term)), &Color::Black);
717 assert_eq!(&uc.attr_fgcolor(Some(term)), &Color::XtermWhite);
718 assert_eq!(&uc.attrib(), &Attrib::Normal);
719
720 // Put a short string "BCD" into the first line of the buffer, with fg color change after the 'B' and bold after 'C'
721 term.clear();
722 term.set_text_fg_color_xterm(XtermColor::White);
723 term.set_text_bg_color_xterm(XtermColor::Black);
724 assert_eq!(term.text_attrib(), Attrib::Normal);
725
726 assert!(term.ansi());
727 term.append("B\x1b[32mC\x1b[1mD\n");
728
729 let r = &term.u8c_disp_row(0);
730 let uc = r.col(0);
731 assert_eq!(uc.text_utf8(), b"B");
732 assert!(uc.is_char(b'B'));
733 assert!(!uc.is_char(b'A'));
734 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
735 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
736 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
737 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
738 assert_eq!(
739 &uc.charflags(),
740 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
741 );
742
743 let uc = r.col(1);
744 assert_eq!(uc.text_utf8(), b"C");
745 assert!(uc.is_char(b'C'));
746 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
747 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
748 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermGreen);
749 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
750 assert_eq!(
751 &uc.charflags(),
752 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
753 );
754
755 let uc = r.col(2);
756 assert_eq!(uc.text_utf8(), b"D");
757 assert!(uc.is_char(b'D'));
758 assert_eq!(&uc.fgcolor(), &Color::XtermGreen);
759 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
760 assert_eq!(&uc.attr_fgcolor(None), &Color::from_rgb(0x20, 0xf0, 0x20));
761 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
762 assert_eq!(
763 &uc.attr_fgcolor(Some(term)),
764 &Color::from_rgb(0x20, 0xf0, 0x20)
765 );
766 assert_eq!(&uc.attr_bgcolor(None), &Color::from_rgb(0x20, 0x20, 0x20));
767 assert_eq!(
768 &uc.charflags(),
769 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
770 );
771
772 // Put a short string "BCDE" into the buffer, with fg color change after the 'B', bg change after 'C', and bold after 'D'
773 term.clear();
774 term.set_text_fg_color_xterm(XtermColor::White);
775 term.set_text_bg_color_xterm(XtermColor::Black);
776 term.set_text_attrib(Attrib::Normal);
777 assert_eq!(term.text_attrib(), Attrib::Normal);
778
779 assert!(term.ansi());
780 term.append("B\x1b[37mC\x1b[44mD\x1b[1mE\n");
781
782 let r = &term.u8c_disp_row(0);
783 let uc = r.col(0);
784 assert_eq!(uc.text_utf8(), b"B");
785 assert!(uc.is_char(b'B'));
786 assert!(!uc.is_char(b'A'));
787 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
788 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
789 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
790 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
791 assert_eq!(
792 &uc.charflags(),
793 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
794 );
795
796 let uc = r.col(1);
797 assert_eq!(uc.text_utf8(), b"C");
798 assert!(uc.is_char(b'C'));
799 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
800 assert_eq!(&uc.bgcolor(), &Color::XtermBlack);
801 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
802 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBlack);
803 assert_eq!(
804 &uc.charflags(),
805 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
806 );
807
808 let uc = r.col(2);
809 assert_eq!(uc.text_utf8(), b"D");
810 assert!(uc.is_char(b'D'));
811 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
812 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
813 assert_eq!(&uc.attr_fgcolor(None), &Color::XtermWhite);
814 assert_eq!(&uc.attr_bgcolor(None), &Color::XtermBgBlue);
815 assert_eq!(
816 &uc.charflags(),
817 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
818 );
819
820 let uc = r.col(3);
821 assert_eq!(uc.text_utf8(), b"E");
822 assert!(uc.is_char(b'E'));
823 assert_eq!(&uc.fgcolor(), &Color::XtermWhite);
824 assert_eq!(&uc.bgcolor(), &Color::XtermBgBlue);
825 assert_eq!(&uc.attr_fgcolor(None), &Color::from_hex(0xf0f0f0));
826 assert_eq!(&uc.attr_bgcolor(None), &Color::from_hex(0x2020e0));
827 assert_eq!(
828 &uc.charflags(),
829 &(CharFlags::FG_XTERM | CharFlags::BG_XTERM)
830 );
831
832 // Test some miscellaneous Utf8 constants
833 assert_eq!(uc.length(), 1);
834 assert_eq!(uc.max_utf8(), 4);
835 assert_eq!(uc.pwidth(), 9.0);
836 assert_eq!(uc.pwidth_int(), 9);
837
838 term.set_text_fg_color_xterm(XtermColor::White);
839 term.set_text_bg_color_xterm(XtermColor::Black);
840 term.clear();
841 term.set_text_attrib(Attrib::Normal);
842
843 // Mouse selection functions
844 term.append(&format!("Mouse selection: {:?}\n", &term.get_selection()));
845 term.clear_mouse_selection();
846 assert_eq!(term.get_selection(), None);
847
848 // Play with cursor position
849 term.append("0123456789\n"); // Set up test pattern
850 term.append("ABCDEFGHIJ\n");
851 term.append("abcdefghij\n");
852
853 term.set_cursor_row(1);
854 assert_eq!(term.cursor_row(), 1);
855 term.set_cursor_col(1);
856 assert_eq!(term.cursor_col(), 1);
857 assert_eq!(term.u8c_cursor().text_utf8(), b"1");
858
859 term.append("----"); // Overwrites text at cursor and moves cursor forward
860 assert_eq!(term.cursor_row(), 1);
861 assert_eq!(term.cursor_col(), 5);
862 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
863 term.set_cursor_col(1);
864 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Overwritten text
865
866 term.cursor_up(1, false);
867 assert_eq!(term.cursor_row(), 0);
868 assert_eq!(term.cursor_col(), 1);
869 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
870
871 // Hit top of screen, so nothing happens
872 term.cursor_up(1, false);
873 assert_eq!(term.cursor_row(), 0);
874 assert_eq!(term.cursor_col(), 1);
875 assert_eq!(term.u8c_cursor().text_utf8(), b"o");
876
877 // Hit top of screen with scroll enabled. A blank line from history is scrolled in.
878 term.cursor_up(1, true);
879 assert_eq!(term.cursor_row(), 0);
880 assert_eq!(term.cursor_col(), 1);
881 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
882
883 // Go back down to the overwritten text
884 term.cursor_down(2, false);
885 assert_eq!(term.cursor_row(), 2);
886 assert_eq!(term.cursor_col(), 1);
887 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
888
889 // Go right past the overwritten text
890 term.cursor_right(4, false);
891 assert_eq!(term.cursor_row(), 2);
892 assert_eq!(term.cursor_col(), 5);
893 assert_eq!(term.u8c_cursor().text_utf8(), b"5");
894
895 // Go left to the end of the overwritten text
896 term.cursor_left(1);
897 assert_eq!(term.cursor_row(), 2);
898 assert_eq!(term.cursor_col(), 4);
899 assert_eq!(term.u8c_cursor().text_utf8(), b"-");
900
901 // Scroll back down, removing the blank line at the top.
902 // Cursor stays in place, the text moves under it.
903 term.scroll(1);
904 assert_eq!(term.cursor_row(), 2);
905 assert_eq!(term.cursor_col(), 4);
906 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
907
908 // Clear from here to end-of-line
909 term.clear_eol();
910 assert_eq!(term.cursor_row(), 2);
911 assert_eq!(term.cursor_col(), 4);
912 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
913
914 // Now clear from here to start-of-line. Cursor does not move.
915 term.clear_sol();
916 assert_eq!(term.cursor_row(), 2);
917 assert_eq!(term.cursor_col(), 4);
918 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
919 term.cursor_left(1);
920 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
921 term.set_cursor_col(0);
922 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
923
924 // Clear some lines
925 term.clear_line(1);
926 assert_eq!(term.cursor_row(), 2);
927 assert_eq!(term.cursor_col(), 0);
928 term.set_cursor_row(1);
929 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
930 term.set_cursor_row(3);
931 term.clear_cur_line();
932 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
933 assert_eq!(term.cursor_row(), 3);
934 assert_eq!(term.cursor_col(), 0);
935
936 term.append("Two lines above are intentionally left blank.\n");
937 assert_eq!(term.cursor_row(), 4);
938 assert_eq!(term.cursor_col(), 0);
939
940 // Set up the test pattern again, then play with insert/delete
941 term.append("0123456789\n");
942 term.append("ABCDEFGHIJ\n");
943 term.append("abcdefghij\n");
944 assert_eq!(term.cursor_row(), 7);
945
946 term.set_cursor_row(4);
947 term.set_cursor_col(4);
948 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
949
950 term.insert_char('x', 5); // Push this row right 5 chars starting at col 4
951 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
952 term.cursor_right(5, false);
953 assert_eq!(term.cursor_col(), 9);
954 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
955
956 // Insert two blank rows above cursor. Cursor stays put.
957 term.insert_rows(2);
958 assert_eq!(term.cursor_row(), 4);
959 assert_eq!(term.cursor_col(), 9);
960 assert_eq!(term.u8c_cursor().text_utf8(), b" ");
961 term.cursor_down(2, false); // Go down to find our text again
962 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
963
964 // Go back to the beginning of the inserted 'x' characters and delete them.
965 term.cursor_left(5);
966 assert_eq!(term.u8c_cursor().text_utf8(), b"x");
967 term.delete_cur_chars(5);
968 assert_eq!(term.cursor_row(), 6);
969 assert_eq!(term.cursor_col(), 4);
970 assert_eq!(term.u8c_cursor().text_utf8(), b"4");
971
972 term.delete_chars(7, 2, 2); // Delete "CD" from the next row
973 term.cursor_down(1, false);
974 term.cursor_left(2);
975 assert_eq!(term.u8c_cursor().text_utf8(), b"E");
976
977 term.delete_rows(1); // Middle row of pattern is gone, cursor stays put
978 assert_eq!(term.u8c_cursor().text_utf8(), b"c");
979 term.cursor_up(1, false);
980 term.delete_rows(2); // Delete remains of test pattern
981
982 term.set_text_attrib(Attrib::Bold);
983 term.insert_char_eol('-', 3, 15, 20);
984 term.set_cursor_row(3);
985 term.set_cursor_col(15);
986 assert_eq!(term.u8c_cursor().text_utf8(), b"-"); // Check the insertion
987 assert_eq!(term.u8c_cursor().attrib(), Attrib::Bold);
988
989 term.set_text_attrib(Attrib::Italic);
990 term.append(" and all lines below");
991 term.set_text_attrib(Attrib::Normal);
992 term.cursor_down(1, false);
993
994 let mut hsb = term.hscrollbar();
995 let mut sb = term.scrollbar();
996 hsb.set_value(100.0);
997 assert_eq!(hsb.value(), 100.0);
998 sb.set_value(50.0);
999 assert_eq!(sb.value(), 50.0);
1000 hsb.set_value(0.0);
1001 assert_eq!(hsb.value(), 0.0);
1002 sb.set_value(0.0);
1003 assert_eq!(sb.value(), 0.0);
1004}Sourcepub fn from_hex_str(col: &str) -> Result<Color, FltkError>
pub fn from_hex_str(col: &str) -> Result<Color, FltkError>
Return a Color from a hex color format (#xxxxxx)
Sourcepub fn to_hex_str(&self) -> String
pub fn to_hex_str(&self) -> String
Returns the color in hex string format
Sourcepub fn by_index(idx: u8) -> Color
pub fn by_index(idx: u8) -> Color
Returns a color by index of RGBI
Examples found in repository?
3fn main() {
4 let app = app::App::default();
5 let mut window = window::Window::default().with_size(300, 300);
6 window.set_frame(FrameType::NoBox);
7 window.make_resizable(true);
8
9 let dx = 20;
10 let dy = dx; // border width of resizable() - see below
11 let tile = group::Tile::default_fill();
12
13 // create the symmetrical resize box with dx and dy pixels distance, resp.
14 // from the borders of the Fl_Tile widget before all other children
15 let r = frame::Frame::new(
16 tile.x() + dx,
17 tile.y() + dy,
18 tile.w() - 2 * dx,
19 tile.h() - 2 * dy,
20 None,
21 );
22 tile.resizable(&r);
23
24 let mut box0 = frame::Frame::new(0, 0, 150, 150, "0");
25 box0.set_frame(FrameType::DownBox);
26 box0.set_color(Color::by_index(9));
27 box0.set_label_size(36);
28 box0.set_align(Align::Clip);
29
30 let mut w1 = window::Window::new(150, 0, 150, 150, "1");
31 w1.set_frame(FrameType::NoBox);
32 let mut box1 = frame::Frame::new(0, 0, 150, 150, "1\nThis is a child window");
33 box1.set_frame(FrameType::DownBox);
34 box1.set_color(Color::by_index(19));
35 box1.set_label_size(18);
36 box1.set_align(Align::Clip | Align::Inside | Align::Wrap);
37 w1.resizable(&box1);
38 w1.end();
39
40 let mut box2a = frame::Frame::new(0, 150, 70, 150, "2a");
41 box2a.set_frame(FrameType::DownBox);
42 box2a.set_color(Color::by_index(12));
43 box2a.set_label_size(36);
44 box2a.set_align(Align::Clip);
45
46 let mut box2b = frame::Frame::new(70, 150, 80, 150, "2b");
47 box2b.set_frame(FrameType::DownBox);
48 box2b.set_color(Color::by_index(13));
49 box2b.set_label_size(36);
50 box2b.set_align(Align::Clip);
51
52 let mut box3a = frame::Frame::new(150, 150, 150, 70, "3a");
53 box3a.set_frame(FrameType::DownBox);
54 box3a.set_color(Color::by_index(12));
55 box3a.set_label_size(36);
56 box3a.set_align(Align::Clip);
57
58 let mut box3b = frame::Frame::new(150, 150 + 70, 150, 80, "3b");
59 box3b.set_frame(FrameType::DownBox);
60 box3b.set_color(Color::by_index(13));
61 box3b.set_label_size(36);
62 box3b.set_align(Align::Clip);
63
64 tile.end();
65 window.end();
66
67 w1.show();
68 window.show();
69
70 app.run().unwrap();
71}Sourcepub fn inactive(&self) -> Color
pub fn inactive(&self) -> Color
Returns an inactive form of the color
Examples found in repository?
5fn create_vertical_gradient_frame(
6 x: i32,
7 y: i32,
8 w: i32,
9 h: i32,
10 col1: Color,
11 col2: Color,
12) -> frame::Frame {
13 let mut frame = frame::Frame::new(x, y, w, h, "Vertical");
14 frame.draw(move |f| {
15 let imax = f.h();
16 let d = if imax > 0 { imax } else { 1 };
17 for i in 0..=imax {
18 let w = 1.0 - i as f32 / d as f32;
19 set_draw_color(Color::inactive(&Color::color_average(col1, col2, w)));
20 draw_xyline(f.x(), f.y() + i, f.x() + f.w());
21 }
22 set_draw_color(Color::Black);
23 set_font(Font::Helvetica, app::font_size());
24 draw_text2(&f.label(), f.x(), f.y(), f.w(), f.h(), f.align());
25 });
26 frame
27}
28
29fn create_horizontal_gradient_frame(
30 x: i32,
31 y: i32,
32 w: i32,
33 h: i32,
34 col1: Color,
35 col2: Color,
36) -> frame::Frame {
37 let mut frame = frame::Frame::new(x, y, w, h, "Horizontal");
38 frame.draw(move |f| {
39 let imax = f.w();
40 let d = if imax > 0 { imax } else { 1 };
41 for i in 0..=imax {
42 let w = 1.0 - i as f32 / d as f32;
43 set_draw_color(Color::inactive(&Color::color_average(col1, col2, w)));
44 draw_yxline(f.x() + i, f.y(), f.y() + f.h());
45 }
46 set_draw_color(Color::Black);
47 set_font(Font::Helvetica, app::font_size());
48 draw_text2(&f.label(), f.x(), f.y(), f.w(), f.h(), f.align());
49 });
50 frame
51}
52
53fn create_horizontal_svg_gradient_frame(
54 x: i32,
55 y: i32,
56 w: i32,
57 h: i32,
58 col1: Color,
59 col2: Color,
60) -> frame::Frame {
61 let mut frame = frame::Frame::new(x, y, w, h, "Svg");
62 frame.draw(move |f| {
63 let (r1, g1, b1) = Color::inactive(&col1).to_rgb();
64 let (r2, g2, b2) = Color::inactive(&col2).to_rgb();
65 let svg = format!(
66 "<svg viewBox='0 0 {} {}'>
67 <defs>
68 <linearGradient id='grad1' x1='0%' y1='0%' x2='0%' y2='100%'>
69 <stop offset='0%' style='stop-color:rgb({},{},{});stop-opacity:1' />
70 <stop offset='100%' style='stop-color:rgb({},{},{});stop-opacity:1' />
71 </linearGradient>
72 </defs>
73 <rect width='100%' height='100%' fill='url(#grad1)' />
74 </svg>",
75 f.w(),
76 f.h() + 1,
77 r1,
78 g1,
79 b1,
80 r2,
81 g2,
82 b2
83 );
84 let mut image = image::SvgImage::from_data(&svg).unwrap();
85 image.draw(f.x(), f.y(), f.w(), f.h());
86 set_draw_color(Color::Black);
87 set_font(Font::Helvetica, app::font_size());
88 draw_text2(&f.label(), f.x(), f.y(), f.w(), f.h(), f.align());
89 });
90 frame
91}Sourcepub fn darker(&self) -> Color
pub fn darker(&self) -> Color
Returns an darker form of the color
Examples found in repository?
3fn main() {
4 let app = app::App::default();
5 // global theming
6 app::background(55, 55, 55); // background color.
7 // For input/output and text widgets, use app::background2
8 app::background2(0, 0, 0);
9 app::foreground(255, 255, 255); // labels
10 app::set_font_size(16);
11 app::set_frame_type2(FrameType::UpBox, FrameType::RFlatBox);
12 app::set_frame_type2(FrameType::DownBox, FrameType::RFlatBox);
13 app::set_frame_border_radius_max(15); // set the roundness of the RFlatBox
14 app::set_font(Font::Times);
15 app::set_visible_focus(false);
16
17 // regular widget code
18 let mut win = window::Window::default().with_size(400, 300);
19 let mut flex = group::Flex::default_fill().column();
20 flex.set_margins(100, 60, 100, 60);
21 flex.set_pad(10);
22 let mut btn1 = button::Button::default().with_label("Increment");
23 btn1.set_color(Color::Red.darker());
24 btn1.set_selection_color(Color::Red.darker().darker());
25 let mut out = frame::Frame::default().with_label("0");
26 out.set_color(Color::Green.darker());
27 let mut btn2 = button::Button::default().with_label("Decrement");
28 btn2.set_color(Color::Red.darker());
29 btn2.set_selection_color(Color::Red.darker().darker());
30 flex.end();
31 win.end();
32 win.show();
33
34 app.run().unwrap();
35}Sourcepub fn gray_ramp(val: i32) -> Color
pub fn gray_ramp(val: i32) -> Color
Returns a gray color value from black (i == 0) to white (i == FL_NUM_GRAY - 1)
Sourcepub fn color_average(c1: Color, c2: Color, weight: f32) -> Color
pub fn color_average(c1: Color, c2: Color, weight: f32) -> Color
Returns a gray color value from black (i == 0) to white (i == FL_NUM_GRAY - 1)
Examples found in repository?
5fn create_vertical_gradient_frame(
6 x: i32,
7 y: i32,
8 w: i32,
9 h: i32,
10 col1: Color,
11 col2: Color,
12) -> frame::Frame {
13 let mut frame = frame::Frame::new(x, y, w, h, "Vertical");
14 frame.draw(move |f| {
15 let imax = f.h();
16 let d = if imax > 0 { imax } else { 1 };
17 for i in 0..=imax {
18 let w = 1.0 - i as f32 / d as f32;
19 set_draw_color(Color::inactive(&Color::color_average(col1, col2, w)));
20 draw_xyline(f.x(), f.y() + i, f.x() + f.w());
21 }
22 set_draw_color(Color::Black);
23 set_font(Font::Helvetica, app::font_size());
24 draw_text2(&f.label(), f.x(), f.y(), f.w(), f.h(), f.align());
25 });
26 frame
27}
28
29fn create_horizontal_gradient_frame(
30 x: i32,
31 y: i32,
32 w: i32,
33 h: i32,
34 col1: Color,
35 col2: Color,
36) -> frame::Frame {
37 let mut frame = frame::Frame::new(x, y, w, h, "Horizontal");
38 frame.draw(move |f| {
39 let imax = f.w();
40 let d = if imax > 0 { imax } else { 1 };
41 for i in 0..=imax {
42 let w = 1.0 - i as f32 / d as f32;
43 set_draw_color(Color::inactive(&Color::color_average(col1, col2, w)));
44 draw_yxline(f.x() + i, f.y(), f.y() + f.h());
45 }
46 set_draw_color(Color::Black);
47 set_font(Font::Helvetica, app::font_size());
48 draw_text2(&f.label(), f.x(), f.y(), f.w(), f.h(), f.align());
49 });
50 frame
51}
52
53fn create_horizontal_svg_gradient_frame(
54 x: i32,
55 y: i32,
56 w: i32,
57 h: i32,
58 col1: Color,
59 col2: Color,
60) -> frame::Frame {
61 let mut frame = frame::Frame::new(x, y, w, h, "Svg");
62 frame.draw(move |f| {
63 let (r1, g1, b1) = Color::inactive(&col1).to_rgb();
64 let (r2, g2, b2) = Color::inactive(&col2).to_rgb();
65 let svg = format!(
66 "<svg viewBox='0 0 {} {}'>
67 <defs>
68 <linearGradient id='grad1' x1='0%' y1='0%' x2='0%' y2='100%'>
69 <stop offset='0%' style='stop-color:rgb({},{},{});stop-opacity:1' />
70 <stop offset='100%' style='stop-color:rgb({},{},{});stop-opacity:1' />
71 </linearGradient>
72 </defs>
73 <rect width='100%' height='100%' fill='url(#grad1)' />
74 </svg>",
75 f.w(),
76 f.h() + 1,
77 r1,
78 g1,
79 b1,
80 r2,
81 g2,
82 b2
83 );
84 let mut image = image::SvgImage::from_data(&svg).unwrap();
85 image.draw(f.x(), f.y(), f.w(), f.h());
86 set_draw_color(Color::Black);
87 set_font(Font::Helvetica, app::font_size());
88 draw_text2(&f.label(), f.x(), f.y(), f.w(), f.h(), f.align());
89 });
90 frame
91}
92
93fn main() {
94 let a = app::App::default();
95 let mut win = window::Window::default().with_size(300, 300);
96 create_vertical_gradient_frame(0, 0, 100, 100, Color::Red, Color::Cyan);
97 create_horizontal_gradient_frame(100, 0, 100, 100, Color::Red, Color::Cyan);
98 create_horizontal_svg_gradient_frame(200, 0, 100, 100, Color::Red, Color::Cyan);
99 win.end();
100 win.draw(|w| {
101 // vertical gradient
102 let imax = w.w();
103 let d = if imax > 0 { imax } else { 1 };
104 for i in 0..=imax {
105 let v = 1.0 - i as f32 / d as f32;
106 set_draw_color(Color::color_average(Color::Red, Color::Blue, v));
107 draw_yxline(i, 0, w.h());
108 }
109 w.draw_children();
110 });
111 win.make_resizable(true);
112 win.show();
113 a.run().unwrap();
114}Sourcepub fn contrast(fg: Color, bg: Color) -> Color
pub fn contrast(fg: Color, bg: Color) -> Color
Returns a color that contrasts with the background color.
Sourcepub fn gray_scale(g: u8) -> Color
pub fn gray_scale(g: u8) -> Color
Returns the color closest to the passed grayscale value
Sourcepub fn rgb_color(r: u8, g: u8, b: u8) -> Color
pub fn rgb_color(r: u8, g: u8, b: u8) -> Color
Returns the color closest to the passed rgb value
Sourcepub fn to_rgb(&self) -> (u8, u8, u8)
pub fn to_rgb(&self) -> (u8, u8, u8)
Get the RGB value of the color
Examples found in repository?
53fn create_horizontal_svg_gradient_frame(
54 x: i32,
55 y: i32,
56 w: i32,
57 h: i32,
58 col1: Color,
59 col2: Color,
60) -> frame::Frame {
61 let mut frame = frame::Frame::new(x, y, w, h, "Svg");
62 frame.draw(move |f| {
63 let (r1, g1, b1) = Color::inactive(&col1).to_rgb();
64 let (r2, g2, b2) = Color::inactive(&col2).to_rgb();
65 let svg = format!(
66 "<svg viewBox='0 0 {} {}'>
67 <defs>
68 <linearGradient id='grad1' x1='0%' y1='0%' x2='0%' y2='100%'>
69 <stop offset='0%' style='stop-color:rgb({},{},{});stop-opacity:1' />
70 <stop offset='100%' style='stop-color:rgb({},{},{});stop-opacity:1' />
71 </linearGradient>
72 </defs>
73 <rect width='100%' height='100%' fill='url(#grad1)' />
74 </svg>",
75 f.w(),
76 f.h() + 1,
77 r1,
78 g1,
79 b1,
80 r2,
81 g2,
82 b2
83 );
84 let mut image = image::SvgImage::from_data(&svg).unwrap();
85 image.draw(f.x(), f.y(), f.w(), f.h());
86 set_draw_color(Color::Black);
87 set_font(Font::Helvetica, app::font_size());
88 draw_text2(&f.label(), f.x(), f.y(), f.w(), f.h(), f.align());
89 });
90 frame
91}More examples
6 pub fn new(radius: i32, mut image: image::RgbImage) -> Self {
7 let mut frame = frame::Frame::new(0, 0, radius * 2, radius * 2, None);
8 frame.set_frame(enums::FrameType::FlatBox);
9 frame.draw(move |f| {
10 image.scale(f.w(), f.h(), false, true);
11 image.draw(f.x(), f.y(), f.w(), f.h());
12 let color = f.color().to_rgb();
13 let s = format!(
14 "<?xml version='1.0' encoding='UTF-8' standalone='no'?>\n
15 <svg width='{}' height='{}'>\n
16 <rect x='{}'
17 y='{}'
18 rx='{}'
19 ry='{}'
20 width='{}'
21 height='{}'
22 fill='none'
23 stroke='rgb({}, {}, {})'
24 stroke-width='{}' />\n
25 </svg>\n",
26 f.w(),
27 f.h(),
28 -f.w() / 2,
29 -f.w() / 2,
30 f.w(),
31 f.w(),
32 f.w() + f.w(),
33 f.h() + f.w(),
34 color.0,
35 color.1,
36 color.2,
37 f.w()
38 );
39 let mut s = image::SvgImage::from_data(&s).unwrap();
40 s.draw(f.x(), f.y(), f.w(), f.h());
41 });
42 Self
43 }122fn main() {
123 let style = Rc::from(RefCell::from(Style::new()));
124
125 let app = App::default().with_scheme(Scheme::Gleam);
126 let mut wind = Window::default()
127 .with_size(500, 200)
128 .with_label("Highlight");
129 let mut vpack = Pack::new(4, 4, 492, 192, "");
130 vpack.set_spacing(4);
131 let mut text_editor = TextEditor::default().with_size(492, 163);
132
133 let mut hpack = Pack::new(4, 4, 492, 25, "").with_type(PackType::Horizontal);
134 hpack.set_spacing(8);
135 let mut font = Choice::default().with_size(130, 25);
136 let mut choice = Choice::default().with_size(130, 25);
137 let mut size = Spinner::default().with_size(60, 25);
138
139 let mut color = Choice::default().with_size(100, 25);
140 let mut btn_clear = Button::default().with_size(40, 25).with_label("X");
141 hpack.end();
142
143 vpack.end();
144 wind.end();
145 wind.show();
146
147 text_editor.wrap_mode(fltk::text::WrapMode::AtBounds, 0);
148 text_editor.set_buffer(TextBuffer::default());
149
150 font.add_choice("Courier|Helvetica|Times");
151 font.set_value(0);
152 font.set_tooltip("Font");
153
154 choice.add_choice("Normal|Underline|Strike");
155 choice.set_value(0);
156
157 size.set_value(18.0);
158 size.set_step(1.0);
159 size.set_range(12.0, 28.0);
160 size.set_tooltip("Size");
161
162 color.set_tooltip("Color");
163 color.add_choice("#000000|#ff0000|#00ff00|#0000ff|#ffff00|#00ffff");
164 color.set_value(0);
165
166 btn_clear.set_label_color(Color::Red);
167 btn_clear.set_tooltip("Clear style");
168
169 // set colors
170 for mut item in color.clone() {
171 if let Some(lbl) = item.label() {
172 item.set_label_color(Color::from_u32(
173 u32::from_str_radix(lbl.trim().strip_prefix('#').unwrap(), 16)
174 .ok()
175 .unwrap(),
176 ));
177 }
178 }
179
180 let style_rc1 = Rc::clone(&style);
181
182 text_editor.buffer().unwrap().add_modify_callback({
183 let mut text_editor1 = text_editor.clone();
184 let font1 = font.clone();
185 let size1 = size.clone();
186 let color1 = color.clone();
187 let choice1 = choice.clone();
188 move |pos: i32, ins_items: i32, del_items: i32, _: i32, _: &str| {
189 let attr = if choice1.value() == 1 {
190 TextAttr::Underline
191 } else if choice1.value() == 2 {
192 TextAttr::StrikeThrough
193 } else {
194 TextAttr::None
195 };
196 if ins_items > 0 || del_items > 0 {
197 let mut style = style_rc1.borrow_mut();
198 let color = Color::from_u32(
199 u32::from_str_radix(
200 color1
201 .text(color1.value())
202 .unwrap()
203 .trim()
204 .strip_prefix('#')
205 .unwrap(),
206 16,
207 )
208 .ok()
209 .unwrap(),
210 );
211 style.apply_style(
212 Some(pos),
213 Some(ins_items),
214 Some(del_items),
215 None,
216 None,
217 Font::by_name(font1.text(font1.value()).unwrap().trim()),
218 size1.value() as i32,
219 color,
220 attr,
221 &mut text_editor1,
222 );
223 }
224 }
225 });
226
227 color.set_callback({
228 let size = size.clone();
229 let font = font.clone();
230 let choice = choice.clone();
231 let mut text_editor = text_editor.clone();
232 let style_rc1 = Rc::clone(&style);
233 move |color| {
234 let attr = match choice.value() {
235 0 => TextAttr::None,
236 1 => TextAttr::Underline,
237 2 => TextAttr::StrikeThrough,
238 _ => unreachable!(),
239 };
240 if let Some(buf) = text_editor.buffer() {
241 if let Some((s, e)) = buf.selection_position() {
242 let mut style = style_rc1.borrow_mut();
243 let color = Color::from_u32(
244 u32::from_str_radix(
245 color
246 .text(color.value())
247 .unwrap()
248 .trim()
249 .strip_prefix('#')
250 .unwrap(),
251 16,
252 )
253 .ok()
254 .unwrap(),
255 );
256 style.apply_style(
257 None,
258 None,
259 None,
260 Some(s),
261 Some(e),
262 Font::by_name(font.text(font.value()).unwrap().trim()),
263 size.value() as i32,
264 color,
265 attr,
266 &mut text_editor,
267 );
268 }
269 }
270 }
271 });
272
273 // get the style from the current cursor position
274 text_editor.handle({
275 let style_rc1 = Rc::clone(&style);
276 let mut font1 = font.clone();
277 let mut size1 = size.clone();
278 let mut color1 = color.clone();
279 move |te, e| match e {
280 Event::KeyUp | Event::Released => {
281 if let Some(buff) = te.style_buffer() {
282 let i = te.insert_position();
283 if let Some(t) = buff.text_range(i, i + 1) {
284 if !t.is_empty() {
285 let style = style_rc1.borrow_mut();
286 if let Some(i) = t.chars().next().map(|c| (c as usize - 65)) {
287 if let Some(style) = style.style_table.get(i) {
288 if let Some(mn) = font1.find_item(&format!("{:?}", style.font))
289 {
290 font1.set_item(&mn);
291 }
292 size1.set_value(style.size as f64);
293 let (r, g, b) = style.color.to_rgb();
294 if let Some(mn) =
295 color1.find_item(format!("{r:02x}{g:02x}{b:02x}").as_str())
296 {
297 color1.set_item(&mn);
298 }
299 }
300 }
301 }
302 }
303 }
304 true
305 }
306 _ => false,
307 }
308 });
309
310 choice.set_callback({
311 let mut color1 = color.clone();
312 move |_| color1.do_callback()
313 });
314
315 font.set_callback({
316 let mut color1 = color.clone();
317 move |_| color1.do_callback()
318 });
319
320 size.set_callback({
321 let mut color1 = color.clone();
322 move |_| color1.do_callback()
323 });
324
325 // clear style of the current selection or, if no text is selected, clear all text style
326 btn_clear.set_callback({
327 let style_rc1 = Rc::clone(&style);
328 let text_editor1 = text_editor.clone();
329 move |_| {
330 match text_editor1.buffer().unwrap().selection_position() {
331 Some((_, _)) => {
332 font.set_value(0);
333 size.set_value(18.0);
334 color.set_value(0);
335 choice.set_value(0);
336 color.do_callback();
337 }
338 None => {
339 font.set_value(0);
340 size.set_value(18.0);
341 color.set_value(0);
342 style_rc1.borrow_mut().apply_style(
343 None,
344 None,
345 None,
346 Some(0),
347 Some(text_editor1.buffer().unwrap().length()),
348 Font::Courier,
349 16,
350 Color::Black,
351 TextAttr::None,
352 &mut text_editor,
353 );
354 }
355 };
356 }
357 });
358
359 app.run().unwrap();
360}