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