Struct fltk::menu::MenuItem

source ·
pub struct MenuItem { /* private fields */ }
Expand description

Creates a menu item

Implementations§

source§

impl MenuItem

source

pub unsafe fn from_ptr(ptr: *mut Fl_Menu_Item) -> MenuItem

Initializes a MenuItem from a pointer

§Safety

The pointer must be valid

source

pub unsafe fn as_ptr(&self) -> *mut Fl_Menu_Item

Returns the inner pointer from a MenuItem

§Safety

Can return multiple mutable pointers to the same item

source

pub fn new(choices: &[&'static str]) -> MenuItem

Initializes a new menu item. This will allocate a static MenuItem, that is expected to live for the entirety of the program.

Examples found in repository?
examples/popup_browser.rs (line 26)
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
fn main() {
    let app = app::App::default().with_scheme(app::Scheme::Gtk);
    app::background(211, 211, 211);

    let mut win = window::Window::default().with_size(900, 300);
    let mut b = browser::HoldBrowser::default()
        .with_size(900 - 10, 300 - 10)
        .center_of(&win);
    let widths = &[50, 50, 50, 70, 70, 40, 40, 70, 70, 50];

    b.set_column_widths(widths);
    b.set_column_char('\t');
    b.add("USER\tPID\t%CPU\t%MEM\tVSZ\tRSS\tTTY\tSTAT\tSTART\tTIME\tCOMMAND");
    b.add("root\t2888\t0.0\t0.0\t1352\t0\ttty3\tSW\tAug15\t0:00\t@b@f/sbin/mingetty tty3");
    b.add("erco\t2889\t0.0\t13.0\t221352\t0\ttty3\tR\tAug15\t1:34\t@b@f/usr/local/bin/render a35 0004");
    b.add("uucp\t2892\t0.0\t0.0\t1352\t0\tttyS0\tSW\tAug15\t0:00\t@b@f/sbin/agetty -h 19200 ttyS0 vt100");
    b.add("root\t13115\t0.0\t0.0\t1352\t0\ttty2\tSW\tAug30\t0:00\t@b@f/sbin/mingetty tty2");
    b.add(
        "root\t13464\t0.0\t0.0\t1352\t0\ttty1\tSW\tAug30\t0:00\t@b@f/sbin/mingetty tty1 --noclear",
    );

    let menu = menu::MenuItem::new(&["1st menu item\t", "2nd menu item\t", "3rd menu item\t"]);

    b.set_callback(move |_| {
        if app::event_mouse_button() == app::MouseButton::Right {
            // or app::event_button() == 3
            let coords = app::event_coords();
            match menu.popup(coords.0, coords.1) {
                None => println!("No value was chosen!"),
                Some(val) => println!("{}", val.label().unwrap()),
            }
        }
    });

    win.make_resizable(true);
    win.end();
    win.show();
    app.run().unwrap();
}
source

pub fn popup(&self, x: i32, y: i32) -> Option<MenuItem>

Creates a popup menu at the specified coordinates and returns its choice

Examples found in repository?
examples/popup_browser.rs (line 32)
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
fn main() {
    let app = app::App::default().with_scheme(app::Scheme::Gtk);
    app::background(211, 211, 211);

    let mut win = window::Window::default().with_size(900, 300);
    let mut b = browser::HoldBrowser::default()
        .with_size(900 - 10, 300 - 10)
        .center_of(&win);
    let widths = &[50, 50, 50, 70, 70, 40, 40, 70, 70, 50];

    b.set_column_widths(widths);
    b.set_column_char('\t');
    b.add("USER\tPID\t%CPU\t%MEM\tVSZ\tRSS\tTTY\tSTAT\tSTART\tTIME\tCOMMAND");
    b.add("root\t2888\t0.0\t0.0\t1352\t0\ttty3\tSW\tAug15\t0:00\t@b@f/sbin/mingetty tty3");
    b.add("erco\t2889\t0.0\t13.0\t221352\t0\ttty3\tR\tAug15\t1:34\t@b@f/usr/local/bin/render a35 0004");
    b.add("uucp\t2892\t0.0\t0.0\t1352\t0\tttyS0\tSW\tAug15\t0:00\t@b@f/sbin/agetty -h 19200 ttyS0 vt100");
    b.add("root\t13115\t0.0\t0.0\t1352\t0\ttty2\tSW\tAug30\t0:00\t@b@f/sbin/mingetty tty2");
    b.add(
        "root\t13464\t0.0\t0.0\t1352\t0\ttty1\tSW\tAug30\t0:00\t@b@f/sbin/mingetty tty1 --noclear",
    );

    let menu = menu::MenuItem::new(&["1st menu item\t", "2nd menu item\t", "3rd menu item\t"]);

    b.set_callback(move |_| {
        if app::event_mouse_button() == app::MouseButton::Right {
            // or app::event_button() == 3
            let coords = app::event_coords();
            match menu.popup(coords.0, coords.1) {
                None => println!("No value was chosen!"),
                Some(val) => println!("{}", val.label().unwrap()),
            }
        }
    });

    win.make_resizable(true);
    win.end();
    win.show();
    app.run().unwrap();
}
source

pub fn pulldown( &self, x: i32, y: i32, w: i32, h: i32, picked: Option<MenuItem>, menu: Option<&impl MenuExt> ) -> Option<MenuItem>

Creates a pulldown menu at the specified coordinates and returns its choice

source

pub fn label(&self) -> Option<String>

Returns the label of the menu item

Examples found in repository?
examples/popup_browser.rs (line 34)
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
fn main() {
    let app = app::App::default().with_scheme(app::Scheme::Gtk);
    app::background(211, 211, 211);

    let mut win = window::Window::default().with_size(900, 300);
    let mut b = browser::HoldBrowser::default()
        .with_size(900 - 10, 300 - 10)
        .center_of(&win);
    let widths = &[50, 50, 50, 70, 70, 40, 40, 70, 70, 50];

    b.set_column_widths(widths);
    b.set_column_char('\t');
    b.add("USER\tPID\t%CPU\t%MEM\tVSZ\tRSS\tTTY\tSTAT\tSTART\tTIME\tCOMMAND");
    b.add("root\t2888\t0.0\t0.0\t1352\t0\ttty3\tSW\tAug15\t0:00\t@b@f/sbin/mingetty tty3");
    b.add("erco\t2889\t0.0\t13.0\t221352\t0\ttty3\tR\tAug15\t1:34\t@b@f/usr/local/bin/render a35 0004");
    b.add("uucp\t2892\t0.0\t0.0\t1352\t0\tttyS0\tSW\tAug15\t0:00\t@b@f/sbin/agetty -h 19200 ttyS0 vt100");
    b.add("root\t13115\t0.0\t0.0\t1352\t0\ttty2\tSW\tAug30\t0:00\t@b@f/sbin/mingetty tty2");
    b.add(
        "root\t13464\t0.0\t0.0\t1352\t0\ttty1\tSW\tAug30\t0:00\t@b@f/sbin/mingetty tty1 --noclear",
    );

    let menu = menu::MenuItem::new(&["1st menu item\t", "2nd menu item\t", "3rd menu item\t"]);

    b.set_callback(move |_| {
        if app::event_mouse_button() == app::MouseButton::Right {
            // or app::event_button() == 3
            let coords = app::event_coords();
            match menu.popup(coords.0, coords.1) {
                None => println!("No value was chosen!"),
                Some(val) => println!("{}", val.label().unwrap()),
            }
        }
    });

    win.make_resizable(true);
    win.end();
    win.show();
    app.run().unwrap();
}
More examples
Hide additional examples
examples/format_text.rs (line 174)
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
fn main() {
    let style = Rc::from(RefCell::from(Style::new()));

    let app = App::default().with_scheme(Scheme::Gleam);
    let mut wind = Window::default()
        .with_size(500, 200)
        .with_label("Highlight");
    let mut vpack = Pack::new(4, 4, 492, 192, "");
    vpack.set_spacing(4);
    let mut text_editor = TextEditor::default().with_size(492, 163);

    let mut hpack = Pack::new(4, 4, 492, 25, "").with_type(PackType::Horizontal);
    hpack.set_spacing(8);
    let mut font = Choice::default().with_size(130, 25);
    let mut choice = Choice::default().with_size(130, 25);
    let mut size = Spinner::default().with_size(60, 25);

    let mut color = Choice::default().with_size(100, 25);
    let mut btn_clear = Button::default().with_size(40, 25).with_label("X");
    hpack.end();

    vpack.end();
    wind.end();
    wind.show();

    text_editor.wrap_mode(fltk::text::WrapMode::AtBounds, 0);
    text_editor.set_buffer(TextBuffer::default());

    font.add_choice("Courier|Helvetica|Times");
    font.set_value(0);
    font.set_tooltip("Font");

    choice.add_choice("Normal|Underline|Strike");
    choice.set_value(0);

    size.set_value(18.0);
    size.set_step(1.0);
    size.set_range(12.0, 28.0);
    size.set_tooltip("Size");

    color.set_tooltip("Color");
    color.add_choice("#000000|#ff0000|#00ff00|#0000ff|#ffff00|#00ffff");
    color.set_value(0);

    btn_clear.set_label_color(Color::Red);
    btn_clear.set_tooltip("Clear style");

    // set colors
    for mut item in color.clone() {
        if let Some(lbl) = item.label() {
            item.set_label_color(Color::from_u32(
                u32::from_str_radix(lbl.trim().strip_prefix('#').unwrap(), 16)
                    .ok()
                    .unwrap(),
            ));
        }
    }

    let style_rc1 = Rc::clone(&style);

    text_editor.buffer().unwrap().add_modify_callback({
        let mut text_editor1 = text_editor.clone();
        let font1 = font.clone();
        let size1 = size.clone();
        let color1 = color.clone();
        let choice1 = choice.clone();
        move |pos: i32, ins_items: i32, del_items: i32, _: i32, _: &str| {
            let attr = if choice1.value() == 1 {
                TextAttr::Underline
            } else if choice1.value() == 2 {
                TextAttr::StrikeThrough
            } else {
                TextAttr::None
            };
            if ins_items > 0 || del_items > 0 {
                let mut style = style_rc1.borrow_mut();
                let color = Color::from_u32(
                    u32::from_str_radix(
                        color1
                            .text(color1.value())
                            .unwrap()
                            .trim()
                            .strip_prefix('#')
                            .unwrap(),
                        16,
                    )
                    .ok()
                    .unwrap(),
                );
                style.apply_style(
                    Some(pos),
                    Some(ins_items),
                    Some(del_items),
                    None,
                    None,
                    Font::by_name(font1.text(font1.value()).unwrap().trim()),
                    size1.value() as i32,
                    color,
                    attr,
                    &mut text_editor1,
                );
            }
        }
    });

    color.set_callback({
        let size = size.clone();
        let font = font.clone();
        let choice = choice.clone();
        let mut text_editor = text_editor.clone();
        let style_rc1 = Rc::clone(&style);
        move |color| {
            let attr = match choice.value() {
                0 => TextAttr::None,
                1 => TextAttr::Underline,
                2 => TextAttr::StrikeThrough,
                _ => unreachable!(),
            };
            if let Some(buf) = text_editor.buffer() {
                if let Some((s, e)) = buf.selection_position() {
                    let mut style = style_rc1.borrow_mut();
                    let color = Color::from_u32(
                        u32::from_str_radix(
                            color
                                .text(color.value())
                                .unwrap()
                                .trim()
                                .strip_prefix('#')
                                .unwrap(),
                            16,
                        )
                        .ok()
                        .unwrap(),
                    );
                    style.apply_style(
                        None,
                        None,
                        None,
                        Some(s),
                        Some(e),
                        Font::by_name(font.text(font.value()).unwrap().trim()),
                        size.value() as i32,
                        color,
                        attr,
                        &mut text_editor,
                    );
                }
            }
        }
    });

    // get the style from the current cursor position
    text_editor.handle({
        let style_rc1 = Rc::clone(&style);
        let mut font1 = font.clone();
        let mut size1 = size.clone();
        let mut color1 = color.clone();
        move |te, e| match e {
            Event::KeyUp | Event::Released => {
                if let Some(buff) = te.style_buffer() {
                    let i = te.insert_position();
                    if let Some(t) = buff.text_range(i, i + 1) {
                        if !t.is_empty() {
                            let style = style_rc1.borrow_mut();
                            if let Some(i) = t.chars().next().map(|c| (c as usize - 65)) {
                                if let Some(style) = style.style_table.get(i) {
                                    if let Some(mn) = font1.find_item(&format!("{:?}", style.font))
                                    {
                                        font1.set_item(&mn);
                                    }
                                    size1.set_value(style.size as f64);
                                    let (r, g, b) = style.color.to_rgb();
                                    if let Some(mn) =
                                        color1.find_item(format!("{r:02x}{g:02x}{b:02x}").as_str())
                                    {
                                        color1.set_item(&mn);
                                    }
                                }
                            }
                        }
                    }
                }
                true
            }
            _ => false,
        }
    });

    choice.set_callback({
        let mut color1 = color.clone();
        move |_| color1.do_callback()
    });

    font.set_callback({
        let mut color1 = color.clone();
        move |_| color1.do_callback()
    });

    size.set_callback({
        let mut color1 = color.clone();
        move |_| color1.do_callback()
    });

    // clear style of the current selection or, if no text is selected, clear all text style
    btn_clear.set_callback({
        let style_rc1 = Rc::clone(&style);
        let text_editor1 = text_editor.clone();
        move |_| {
            match text_editor1.buffer().unwrap().selection_position() {
                Some((_, _)) => {
                    font.set_value(0);
                    size.set_value(18.0);
                    color.set_value(0);
                    choice.set_value(0);
                    color.do_callback();
                }
                None => {
                    font.set_value(0);
                    size.set_value(18.0);
                    color.set_value(0);
                    style_rc1.borrow_mut().apply_style(
                        None,
                        None,
                        None,
                        Some(0),
                        Some(text_editor1.buffer().unwrap().length()),
                        Font::Courier,
                        16,
                        Color::Black,
                        TextAttr::None,
                        &mut text_editor,
                    );
                }
            };
        }
    });

    app.run().unwrap();
}
source

pub fn set_label(&mut self, txt: &str)

Sets the label of the menu item

source

pub fn label_type(&self) -> LabelType

Returns the label type of the menu item

source

pub fn set_label_type(&mut self, typ: LabelType)

Sets the label type of the menu item

source

pub fn label_color(&self) -> Color

Returns the label color of the menu item

source

pub fn set_label_color(&mut self, color: Color)

Sets the label color of the menu item

Examples found in repository?
examples/editor2.rs (line 338)
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
    pub fn save_file(&mut self) -> Result<bool, Box<dyn error::Error>> {
        match &self.filename {
            Some(f) => {
                self.buf.save_file(f)?;
                self.modified = false;
                self.menu
                    .menu
                    .find_item("&File/Save\t")
                    .unwrap()
                    .deactivate();
                self.menu
                    .menu
                    .find_item("&File/Quit\t")
                    .unwrap()
                    .set_label_color(Color::Black);
                let name = match &self.filename {
                    Some(f) => f.to_string_lossy().to_string(),
                    None => "(Untitled)".to_string(),
                };
                self.main_win.set_label(&format!("{name} - RustyEd"));
                Ok(true)
            }
            None => self.save_file_as(),
        }
    }

    /** Called by "Save As..." or by "Save" in case no file was set yet.
     * Returns true if the file was succesfully saved. */
    pub fn save_file_as(&mut self) -> Result<bool, Box<dyn error::Error>> {
        if let Some(c) = nfc_get_file(dialog::NativeFileChooserType::BrowseSaveFile) {
            self.buf.save_file(&c)?;
            self.modified = false;
            self.menu
                .menu
                .find_item("&File/Save\t")
                .unwrap()
                .deactivate();
            self.menu
                .menu
                .find_item("&File/Quit\t")
                .unwrap()
                .set_label_color(Color::Black);
            self.filename = Some(c);
            self.main_win
                .set_label(&format!("{:?} - RustyEd", self.filename.as_ref().unwrap()));
            Ok(true)
        } else {
            Ok(false)
        }
    }

    pub fn launch(&mut self) {
        while self.app.wait() {
            use Message::*;
            if let Some(msg) = self.r.recv() {
                match msg {
                    Changed => {
                        if !self.modified {
                            self.modified = true;
                            self.menu.menu.find_item("&File/Save\t").unwrap().activate();
                            self.menu.menu.find_item("&File/Quit\t").unwrap().set_label_color(Color::Red);
                            let name = match &self.filename {
                                Some(f) => f.to_string_lossy().to_string(),
                                None => "(Untitled)".to_string(),
                            };
                            self.main_win.set_label(&format!("* {name} - RustyEd"));
                        }
                    }
                    New => {
                        if self.buf.text() != "" {
                            let clear = if let Some(x) = dialog::choice2(center().0 - 200, center().1 - 100, "File unsaved, Do you wish to continue?", "Yes", "No!", "") {
                                x == 0
                            } else {
                                false
                            };
                            if clear {
                                self.buf.set_text("");
                            }
                        }
                    },
                    Open => {
                        if let Some(c) = nfc_get_file(dialog::NativeFileChooserType::BrowseFile) {
                            if c.exists() {
                                match self.buf.load_file(&c) {
                                    Ok(_) => self.filename = Some(c),
                                    Err(e) => dialog::alert(center().0 - 200, center().1 - 100, &format!("An issue occured while loading the file: {e}")),
                                }
                            } else {
                                dialog::alert(center().0 - 200, center().1 - 100, "File does not exist!")
                            }
                        }
                    },
                    Save => { self.save_file().unwrap(); },
                    SaveAs => { self.save_file_as().unwrap(); },
                    Print => {
                        let mut printer = printer::Printer::default();
                        if printer.begin_job(0).is_ok() {
                            let (w, h) = printer.printable_rect();
                            self.printable.set_size(w - 40, h - 40);
                            // Needs cleanup
                            let line_count = self.printable.count_lines(0, self.printable.buffer().unwrap().length(), true) / 45;
                            for i in 0..=line_count {
                                self.printable.scroll(45 * i, 0);
                                printer.begin_page().ok();
                                printer.print_widget(&self.printable, 20, 20);
                                printer.end_page().ok();
                            }
                            printer.end_job();
                        }
                    },
                    Quit => {
                        if self.modified {
                            match dialog::choice2(center().0 - 200, center().1 - 100,
                                "Would you like to save your work?", "Yes", "No", "") {
                                Some(0) => {
                                    if self.save_file().unwrap() {
                                        self.app.quit();
                                    }
                                },
                                Some(1) => { self.app.quit() },
                                Some(_) | None  => (),
                            }
                        } else {
                            self.app.quit();
                        }
                    },
                    Cut => self.editor.cut(),
                    Copy => self.editor.copy(),
                    Paste => self.editor.paste(),
                    About => dialog::message(center().0 - 300, center().1 - 100, "This is an example application written in Rust and using the FLTK Gui library."),
                }
            }
        }
    }
More examples
Hide additional examples
examples/editor.rs (line 55)
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
fn init_menu(m: &mut menu::SysMenuBar) {
    m.add(
        "&File/New...\t",
        Shortcut::Ctrl | 'n',
        menu::MenuFlag::Normal,
        menu_cb,
    );
    m.add(
        "&File/Open...\t",
        Shortcut::Ctrl | 'o',
        menu::MenuFlag::Normal,
        menu_cb,
    );
    m.add(
        "&File/Save\t",
        Shortcut::Ctrl | 's',
        menu::MenuFlag::Normal,
        menu_cb,
    );
    m.add(
        "&File/Save as...\t",
        Shortcut::Ctrl | 'w',
        menu::MenuFlag::MenuDivider,
        menu_cb,
    );
    let idx = m.add(
        "&File/Quit\t",
        Shortcut::Ctrl | 'q',
        menu::MenuFlag::Normal,
        menu_cb,
    );
    m.at(idx).unwrap().set_label_color(Color::Red);
    m.add(
        "&Edit/Cut\t",
        Shortcut::Ctrl | 'x',
        menu::MenuFlag::Normal,
        menu_cb,
    );
    m.add(
        "&Edit/Copy\t",
        Shortcut::Ctrl | 'c',
        menu::MenuFlag::Normal,
        menu_cb,
    );
    m.add(
        "&Edit/Paste\t",
        Shortcut::Ctrl | 'v',
        menu::MenuFlag::Normal,
        menu_cb,
    );
    m.add(
        "&Help/About\t",
        Shortcut::None,
        menu::MenuFlag::Normal,
        menu_cb,
    );
}
examples/format_text.rs (lines 175-179)
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
fn main() {
    let style = Rc::from(RefCell::from(Style::new()));

    let app = App::default().with_scheme(Scheme::Gleam);
    let mut wind = Window::default()
        .with_size(500, 200)
        .with_label("Highlight");
    let mut vpack = Pack::new(4, 4, 492, 192, "");
    vpack.set_spacing(4);
    let mut text_editor = TextEditor::default().with_size(492, 163);

    let mut hpack = Pack::new(4, 4, 492, 25, "").with_type(PackType::Horizontal);
    hpack.set_spacing(8);
    let mut font = Choice::default().with_size(130, 25);
    let mut choice = Choice::default().with_size(130, 25);
    let mut size = Spinner::default().with_size(60, 25);

    let mut color = Choice::default().with_size(100, 25);
    let mut btn_clear = Button::default().with_size(40, 25).with_label("X");
    hpack.end();

    vpack.end();
    wind.end();
    wind.show();

    text_editor.wrap_mode(fltk::text::WrapMode::AtBounds, 0);
    text_editor.set_buffer(TextBuffer::default());

    font.add_choice("Courier|Helvetica|Times");
    font.set_value(0);
    font.set_tooltip("Font");

    choice.add_choice("Normal|Underline|Strike");
    choice.set_value(0);

    size.set_value(18.0);
    size.set_step(1.0);
    size.set_range(12.0, 28.0);
    size.set_tooltip("Size");

    color.set_tooltip("Color");
    color.add_choice("#000000|#ff0000|#00ff00|#0000ff|#ffff00|#00ffff");
    color.set_value(0);

    btn_clear.set_label_color(Color::Red);
    btn_clear.set_tooltip("Clear style");

    // set colors
    for mut item in color.clone() {
        if let Some(lbl) = item.label() {
            item.set_label_color(Color::from_u32(
                u32::from_str_radix(lbl.trim().strip_prefix('#').unwrap(), 16)
                    .ok()
                    .unwrap(),
            ));
        }
    }

    let style_rc1 = Rc::clone(&style);

    text_editor.buffer().unwrap().add_modify_callback({
        let mut text_editor1 = text_editor.clone();
        let font1 = font.clone();
        let size1 = size.clone();
        let color1 = color.clone();
        let choice1 = choice.clone();
        move |pos: i32, ins_items: i32, del_items: i32, _: i32, _: &str| {
            let attr = if choice1.value() == 1 {
                TextAttr::Underline
            } else if choice1.value() == 2 {
                TextAttr::StrikeThrough
            } else {
                TextAttr::None
            };
            if ins_items > 0 || del_items > 0 {
                let mut style = style_rc1.borrow_mut();
                let color = Color::from_u32(
                    u32::from_str_radix(
                        color1
                            .text(color1.value())
                            .unwrap()
                            .trim()
                            .strip_prefix('#')
                            .unwrap(),
                        16,
                    )
                    .ok()
                    .unwrap(),
                );
                style.apply_style(
                    Some(pos),
                    Some(ins_items),
                    Some(del_items),
                    None,
                    None,
                    Font::by_name(font1.text(font1.value()).unwrap().trim()),
                    size1.value() as i32,
                    color,
                    attr,
                    &mut text_editor1,
                );
            }
        }
    });

    color.set_callback({
        let size = size.clone();
        let font = font.clone();
        let choice = choice.clone();
        let mut text_editor = text_editor.clone();
        let style_rc1 = Rc::clone(&style);
        move |color| {
            let attr = match choice.value() {
                0 => TextAttr::None,
                1 => TextAttr::Underline,
                2 => TextAttr::StrikeThrough,
                _ => unreachable!(),
            };
            if let Some(buf) = text_editor.buffer() {
                if let Some((s, e)) = buf.selection_position() {
                    let mut style = style_rc1.borrow_mut();
                    let color = Color::from_u32(
                        u32::from_str_radix(
                            color
                                .text(color.value())
                                .unwrap()
                                .trim()
                                .strip_prefix('#')
                                .unwrap(),
                            16,
                        )
                        .ok()
                        .unwrap(),
                    );
                    style.apply_style(
                        None,
                        None,
                        None,
                        Some(s),
                        Some(e),
                        Font::by_name(font.text(font.value()).unwrap().trim()),
                        size.value() as i32,
                        color,
                        attr,
                        &mut text_editor,
                    );
                }
            }
        }
    });

    // get the style from the current cursor position
    text_editor.handle({
        let style_rc1 = Rc::clone(&style);
        let mut font1 = font.clone();
        let mut size1 = size.clone();
        let mut color1 = color.clone();
        move |te, e| match e {
            Event::KeyUp | Event::Released => {
                if let Some(buff) = te.style_buffer() {
                    let i = te.insert_position();
                    if let Some(t) = buff.text_range(i, i + 1) {
                        if !t.is_empty() {
                            let style = style_rc1.borrow_mut();
                            if let Some(i) = t.chars().next().map(|c| (c as usize - 65)) {
                                if let Some(style) = style.style_table.get(i) {
                                    if let Some(mn) = font1.find_item(&format!("{:?}", style.font))
                                    {
                                        font1.set_item(&mn);
                                    }
                                    size1.set_value(style.size as f64);
                                    let (r, g, b) = style.color.to_rgb();
                                    if let Some(mn) =
                                        color1.find_item(format!("{r:02x}{g:02x}{b:02x}").as_str())
                                    {
                                        color1.set_item(&mn);
                                    }
                                }
                            }
                        }
                    }
                }
                true
            }
            _ => false,
        }
    });

    choice.set_callback({
        let mut color1 = color.clone();
        move |_| color1.do_callback()
    });

    font.set_callback({
        let mut color1 = color.clone();
        move |_| color1.do_callback()
    });

    size.set_callback({
        let mut color1 = color.clone();
        move |_| color1.do_callback()
    });

    // clear style of the current selection or, if no text is selected, clear all text style
    btn_clear.set_callback({
        let style_rc1 = Rc::clone(&style);
        let text_editor1 = text_editor.clone();
        move |_| {
            match text_editor1.buffer().unwrap().selection_position() {
                Some((_, _)) => {
                    font.set_value(0);
                    size.set_value(18.0);
                    color.set_value(0);
                    choice.set_value(0);
                    color.do_callback();
                }
                None => {
                    font.set_value(0);
                    size.set_value(18.0);
                    color.set_value(0);
                    style_rc1.borrow_mut().apply_style(
                        None,
                        None,
                        None,
                        Some(0),
                        Some(text_editor1.buffer().unwrap().length()),
                        Font::Courier,
                        16,
                        Color::Black,
                        TextAttr::None,
                        &mut text_editor,
                    );
                }
            };
        }
    });

    app.run().unwrap();
}
source

pub fn label_font(&self) -> Font

Returns the label font of the menu item

source

pub fn set_label_font(&mut self, font: Font)

Sets the label font of the menu item

source

pub fn label_size(&self) -> i32

Returns the label size of the menu item

source

pub fn set_label_size(&mut self, sz: i32)

Sets the label size of the menu item

source

pub fn value(&self) -> bool

Returns the value of the menu item

source

pub fn set(&mut self)

Sets the menu item

source

pub fn clear(&mut self)

Turns the check or radio item “off” for the menu item

source

pub fn visible(&self) -> bool

Returns whether the menu item is visible or not

source

pub fn active(&self) -> bool

Returns whether the menu item is active

source

pub fn activate(&mut self)

Activates the menu item

Examples found in repository?
examples/editor2.rs (line 383)
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
    pub fn launch(&mut self) {
        while self.app.wait() {
            use Message::*;
            if let Some(msg) = self.r.recv() {
                match msg {
                    Changed => {
                        if !self.modified {
                            self.modified = true;
                            self.menu.menu.find_item("&File/Save\t").unwrap().activate();
                            self.menu.menu.find_item("&File/Quit\t").unwrap().set_label_color(Color::Red);
                            let name = match &self.filename {
                                Some(f) => f.to_string_lossy().to_string(),
                                None => "(Untitled)".to_string(),
                            };
                            self.main_win.set_label(&format!("* {name} - RustyEd"));
                        }
                    }
                    New => {
                        if self.buf.text() != "" {
                            let clear = if let Some(x) = dialog::choice2(center().0 - 200, center().1 - 100, "File unsaved, Do you wish to continue?", "Yes", "No!", "") {
                                x == 0
                            } else {
                                false
                            };
                            if clear {
                                self.buf.set_text("");
                            }
                        }
                    },
                    Open => {
                        if let Some(c) = nfc_get_file(dialog::NativeFileChooserType::BrowseFile) {
                            if c.exists() {
                                match self.buf.load_file(&c) {
                                    Ok(_) => self.filename = Some(c),
                                    Err(e) => dialog::alert(center().0 - 200, center().1 - 100, &format!("An issue occured while loading the file: {e}")),
                                }
                            } else {
                                dialog::alert(center().0 - 200, center().1 - 100, "File does not exist!")
                            }
                        }
                    },
                    Save => { self.save_file().unwrap(); },
                    SaveAs => { self.save_file_as().unwrap(); },
                    Print => {
                        let mut printer = printer::Printer::default();
                        if printer.begin_job(0).is_ok() {
                            let (w, h) = printer.printable_rect();
                            self.printable.set_size(w - 40, h - 40);
                            // Needs cleanup
                            let line_count = self.printable.count_lines(0, self.printable.buffer().unwrap().length(), true) / 45;
                            for i in 0..=line_count {
                                self.printable.scroll(45 * i, 0);
                                printer.begin_page().ok();
                                printer.print_widget(&self.printable, 20, 20);
                                printer.end_page().ok();
                            }
                            printer.end_job();
                        }
                    },
                    Quit => {
                        if self.modified {
                            match dialog::choice2(center().0 - 200, center().1 - 100,
                                "Would you like to save your work?", "Yes", "No", "") {
                                Some(0) => {
                                    if self.save_file().unwrap() {
                                        self.app.quit();
                                    }
                                },
                                Some(1) => { self.app.quit() },
                                Some(_) | None  => (),
                            }
                        } else {
                            self.app.quit();
                        }
                    },
                    Cut => self.editor.cut(),
                    Copy => self.editor.copy(),
                    Paste => self.editor.paste(),
                    About => dialog::message(center().0 - 300, center().1 - 100, "This is an example application written in Rust and using the FLTK Gui library."),
                }
            }
        }
    }
source

pub fn deactivate(&mut self)

Deactivates the menu item

Examples found in repository?
examples/editor2.rs (line 218)
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
    pub fn new(args: Vec<String>) -> Self {
        let app = app::App::default().with_scheme(app::Scheme::Gtk);
        app::background(211, 211, 211);
        let (s, r) = app::channel::<Message>();
        let mut buf = text::TextBuffer::default();
        buf.set_tab_distance(4);
        let mut main_win = window::Window::default()
            .with_size(800, 600)
            .center_screen()
            .with_label("RustyEd");
        let menu = MyMenu::new(&s);
        let modified = false;
        menu.menu.find_item("&File/Save\t").unwrap().deactivate();
        let mut editor = MyEditor::new(buf.clone());
        editor.emit(s, Message::Changed);
        main_win.make_resizable(true);
        // only resize editor, not the menu bar
        main_win.resizable(&*editor);
        main_win.end();
        main_win.show();
        main_win.set_callback(move |_| {
            if app::event() == Event::Close {
                s.send(Message::Quit);
            }
        });
        let filename = if args.len() > 1 {
            let file = path::Path::new(&args[1]);
            assert!(
                file.exists() && file.is_file(),
                "An error occurred while opening the file!"
            );
            match buf.load_file(&args[1]) {
                Ok(_) => Some(PathBuf::from(args[1].clone())),
                Err(e) => {
                    dialog::alert(
                        center().0 - 200,
                        center().1 - 100,
                        &format!("An issue occured while loading the file: {e}"),
                    );
                    None
                }
            }
        } else {
            None
        };

        // Handle drag and drop
        editor.handle({
            let mut dnd = false;
            let mut released = false;
            let buf = buf.clone();
            move |_, ev| match ev {
                Event::DndEnter => {
                    dnd = true;
                    true
                }
                Event::DndDrag => true,
                Event::DndRelease => {
                    released = true;
                    true
                }
                Event::Paste => {
                    if dnd && released {
                        let path = app::event_text();
                        let path = path.trim();
                        let path = path.replace("file://", "");
                        let path = std::path::PathBuf::from(&path);
                        if path.exists() {
                            // we use a timeout to avoid pasting the path into the buffer
                            app::add_timeout3(0.0, {
                                let mut buf = buf.clone();
                                move |_| match buf.load_file(&path) {
                                    Ok(_) => (),
                                    Err(e) => dialog::alert(
                                        center().0 - 200,
                                        center().1 - 100,
                                        &format!("An issue occured while loading the file: {e}"),
                                    ),
                                }
                            });
                        }
                        dnd = false;
                        released = false;
                        true
                    } else {
                        false
                    }
                }
                Event::DndLeave => {
                    dnd = false;
                    released = false;
                    true
                }
                _ => false,
            }
        });

        // What shows when we attempt to print
        let mut printable = text::TextDisplay::default();
        printable.set_frame(FrameType::NoBox);
        printable.set_scrollbar_size(0);
        printable.set_buffer(Some(buf.clone()));

        Self {
            app,
            modified,
            filename,
            r,
            main_win,
            menu,
            buf,
            editor,
            printable,
        }
    }

    /** Called by "Save", test if file can be written, otherwise call save_file_as()
     * afterwards. Will return true if the file is succesfully saved. */
    pub fn save_file(&mut self) -> Result<bool, Box<dyn error::Error>> {
        match &self.filename {
            Some(f) => {
                self.buf.save_file(f)?;
                self.modified = false;
                self.menu
                    .menu
                    .find_item("&File/Save\t")
                    .unwrap()
                    .deactivate();
                self.menu
                    .menu
                    .find_item("&File/Quit\t")
                    .unwrap()
                    .set_label_color(Color::Black);
                let name = match &self.filename {
                    Some(f) => f.to_string_lossy().to_string(),
                    None => "(Untitled)".to_string(),
                };
                self.main_win.set_label(&format!("{name} - RustyEd"));
                Ok(true)
            }
            None => self.save_file_as(),
        }
    }

    /** Called by "Save As..." or by "Save" in case no file was set yet.
     * Returns true if the file was succesfully saved. */
    pub fn save_file_as(&mut self) -> Result<bool, Box<dyn error::Error>> {
        if let Some(c) = nfc_get_file(dialog::NativeFileChooserType::BrowseSaveFile) {
            self.buf.save_file(&c)?;
            self.modified = false;
            self.menu
                .menu
                .find_item("&File/Save\t")
                .unwrap()
                .deactivate();
            self.menu
                .menu
                .find_item("&File/Quit\t")
                .unwrap()
                .set_label_color(Color::Black);
            self.filename = Some(c);
            self.main_win
                .set_label(&format!("{:?} - RustyEd", self.filename.as_ref().unwrap()));
            Ok(true)
        } else {
            Ok(false)
        }
    }
source

pub fn is_submenu(&self) -> bool

Returns whether a menu item is a submenu

source

pub fn is_checkbox(&self) -> bool

Returns whether a menu item is a checkbox

source

pub fn is_radio(&self) -> bool

Returns whether a menu item is a radio item

source

pub fn show(&mut self)

Shows the menu item

source

pub fn hide(&mut self)

Hides the menu item

source

pub fn next(&self, idx: i32) -> Option<MenuItem>

Get the next menu item skipping submenus

source

pub fn children(&self) -> i32

Get children of MenuItem

source

pub fn submenus(&self) -> i32

Get the submenu count

source

pub fn size(&self) -> i32

Get the size of the MenuItem

source

pub fn at(&self, idx: i32) -> Option<MenuItem>

Get the menu item at idx

source

pub unsafe fn user_data(&self) -> Option<Box<dyn FnMut()>>

Get the user data

§Safety

Can return multiple mutable instances of the user data, which has a different lifetime than the object

source

pub fn set_callback<F: FnMut(&mut Choice) + 'static>(&mut self, cb: F)

Set a callback for the menu item

Examples found in repository?
examples/terminal.rs (lines 46-49)
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
fn main() {
    let app = fltk::app::App::default();

    // Set panic handler for main thread (will become UI thread)
    std::panic::set_hook(Box::new({
        |e| {
            eprintln!("!!!!PANIC!!!!{:#?}", e);
            error_box(e.to_string()); // Only works from the UI thread
            std::process::exit(2);
        }
    }));

    let mut main_win = Window::new(
        2285,
        180,
        WIN_WIDTH,
        WIN_HEIGHT,
        "FLTK/Terminal Rust wrapper test",
    );
    main_win.set_type(WindowType::Double);
    main_win.make_resizable(true);

    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);

    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
    term.set_label("term");
    main_win.resizable(&term);
    term.set_label_type(LabelType::None);

    let idx = menu_bar.add_choice("Test&1");
    menu_bar.at(idx).unwrap().set_callback({
        let mut term1 = term.clone();
        move |c| mb_test1_cb(c, &mut term1)
    });
    menu_bar
        .at(idx)
        .unwrap()
        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1

    let idx = menu_bar.add_choice("Test&2");
    menu_bar.at(idx).unwrap().set_callback({
        let mut term1 = term.clone();
        move |c| mb_test2_cb(c, &mut term1)
    });
    menu_bar
        .at(idx)
        .unwrap()
        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2

    let idx = menu_bar.add_choice("Test&3");
    menu_bar.at(idx).unwrap().set_callback({
        let mut term1 = term.clone();
        move |c| mb_test3_cb(c, &mut term1)
    });
    menu_bar
        .at(idx)
        .unwrap()
        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3

    let idx = menu_bar.add_choice("Test&4");
    menu_bar.at(idx).unwrap().set_callback({
        let mut term1 = term.clone();
        move |c| mb_test4_cb(c, &mut term1)
    });
    menu_bar
        .at(idx)
        .unwrap()
        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4

    let idx = menu_bar.add_choice("Test&5");
    menu_bar.at(idx).unwrap().set_callback({
        let mut term1 = term.clone();
        move |c| mb_test5_cb(c, &mut term1)
    });
    menu_bar
        .at(idx)
        .unwrap()
        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5

    menu_bar.end();

    main_win.end();
    main_win.show();

    // Worker thread that drives the startup tests
    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
        let mut term = term.clone();
        move || {
            println!("Startup tests\n");
            term.append("Startup tests\n\n");

            // Testing ansi() and set_ansi() methods
            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
            term.set_ansi(false);
            assert!(!term.ansi());
            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
            term.append_u8(b"Appending u8 array\n");
            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
            term.set_ansi(true); // Restore ANSI state

            // Test show_unknown() as incidental part of testing append methods
            term.set_show_unknown(true);
            assert!(term.show_unknown());
            term.append_ascii(
                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
            );
            term.set_show_unknown(false);
            assert!(!term.show_unknown());

            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");

            let r = term.cursor_row();
            assert_eq!(term.cursor_col(), 0);
            term.append(&format!("Testing cursor row/col {r}"));
            assert_eq!(term.cursor_col(), 24);
            assert_eq!(term.cursor_row(), r);

            // Test cursor color methods
            assert_eq!(
                term.cursor_bg_color(),
                Color::XtermGreen,
                "Default cursor bg at startup"
            );
            assert_eq!(
                term.cursor_fg_color(),
                Color::from_hex(0xff_ff_f0),
                "Default cursor fg at startup"
            );
            term.set_cursor_bg_color(Color::Red);
            assert_eq!(term.cursor_bg_color(), Color::Red);
            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
            term.set_cursor_fg_color(Color::Blue);
            assert_eq!(term.cursor_bg_color(), Color::Red);
            assert_eq!(term.cursor_fg_color(), Color::Blue);
            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));

            // The default display_rows() will derive from the window size
            let dr = term.display_rows();
            assert!(dr > 20, "Default display_rows at startup");
            term.set_display_rows(60);
            assert_eq!(term.display_rows(), 60);
            term.set_display_rows(dr); // Set back to default
            assert_eq!(term.display_rows(), dr);

            // The default display_columns() will derive from the window size
            let dc = term.display_columns();
            assert!(dc > 80, "Default display_rows at startup");
            term.set_display_columns(200);
            assert_eq!(term.display_columns(), 200);
            term.append("\n         1         2         3         4         5         6         7         8         9");
            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
            term.set_display_columns(90);
            assert_eq!(term.display_columns(), 90);
            term.set_display_columns(dc); // Set back to default
            assert_eq!(term.display_columns(), dc);

            let hl = term.history_lines();
            assert_eq!(hl, 100, "Default history_lines at startup");
            term.set_history_lines(60);
            assert_eq!(term.history_lines(), 60);
            term.set_history_lines(hl); // Set back to default
            assert_eq!(term.history_lines(), hl);

            // Is history_rows() an alias for history_lines()?
            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
            term.set_history_rows(50);
            assert_eq!(term.history_rows(), 50);
            term.set_history_lines(100); // Set back to default
            assert_eq!(term.history_lines(), 100);

            let hu = term.history_use();
            term.append(&format!(
                "history_use = {hu} (it's not clear what this means)\n"
            ));
            // assert_eq!(term.history_use(), hu+1);

            term.append(&format!(
                "margins = b:{} l:{} r:{} t{}\n",
                term.margin_bottom(),
                term.margin_left(),
                term.margin_right(),
                term.margin_top()
            ));
            assert_eq!(term.margin_bottom(), 3);
            assert_eq!(term.margin_left(), 3);
            assert_eq!(term.margin_right(), 3);
            assert_eq!(term.margin_top(), 3);

            term.set_margin_bottom(5);
            term.set_margin_left(10);
            term.set_margin_right(15);
            term.set_margin_top(20);
            assert_eq!(term.margin_bottom(), 5);
            assert_eq!(term.margin_left(), 10);
            assert_eq!(term.margin_right(), 15);
            assert_eq!(term.margin_top(), 20);

            term.append("Single character: '");
            term.print_char('X');
            term.append("', single UTF-8 character: '");
            term.print_char_utf8('↑');
            term.append("'\n");

            let rr = term.redraw_rate();
            assert_eq!(rr, 0.1, "Default redraw rate at startup");
            term.append(&format!("Redraw rate {rr}\n"));
            term.set_redraw_rate(1.0);
            assert_eq!(term.redraw_rate(), 1.0);
            term.set_redraw_rate(rr);
            assert_eq!(term.redraw_rate(), rr);

            let rs = term.redraw_style();
            term.append(&format!("Redraw style {rs:?}\n"));
            assert_eq!(
                rs,
                RedrawStyle::RateLimited,
                "Default redraw style at startup"
            );
            term.set_redraw_style(RedrawStyle::NoRedraw);
            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
            term.set_redraw_style(rs);
            assert_eq!(term.redraw_style(), rs);

            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
            assert_eq!(
                RedrawStyle::NoRedraw.bits(),
                0x0000,
                "RedrawStyle enum values have been reassigned"
            );
            assert_eq!(
                RedrawStyle::RateLimited.bits(),
                0x0001,
                "RedrawStyle enum values have been reassigned"
            );
            assert_eq!(
                RedrawStyle::PerWrite.bits(),
                0x0002,
                "RedrawStyle enum values have been reassigned"
            );

            term.append(&format!(
                "Scrollbar actual size {}\n",
                term.scrollbar_actual_size()
            ));
            assert_eq!(term.scrollbar_actual_size(), 16);
            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
            assert_eq!(
                term.scrollbar_size(),
                0,
                "Default scrollbar size at startup"
            );
            term.set_scrollbar_size(40);
            assert_eq!(term.scrollbar_size(), 40);
            assert_eq!(term.scrollbar_actual_size(), 40);
            term.append(&format!(
                "Scrollbar actual size {}\n",
                term.scrollbar_actual_size()
            ));
            term.set_scrollbar_size(0); // Restore default
            assert_eq!(term.scrollbar_size(), 0);
            assert_eq!(term.scrollbar_actual_size(), 16);

            let sfc = term.selection_fg_color();
            let sbc = term.selection_bg_color();
            assert_eq!(sfc, Color::Black);
            assert_eq!(sbc, Color::White);
            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
            term.set_selection_fg_color(Color::Green);
            term.set_selection_bg_color(Color::DarkBlue);
            assert_eq!(term.selection_fg_color(), Color::Green);
            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
            term.set_selection_fg_color(sfc);
            term.set_selection_bg_color(sbc);
            assert_eq!(term.selection_fg_color(), Color::Black);
            assert_eq!(term.selection_bg_color(), Color::White);

            let tfcd = term.text_fg_color_default();
            let tbcd = term.text_bg_color_default();
            assert_eq!(tfcd, Color::XtermWhite);
            assert_eq!(tbcd, Color::TransparentBg);
            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
            term.set_text_fg_color_default(Color::Green);
            term.set_text_bg_color_default(Color::DarkBlue);
            assert_eq!(term.text_fg_color_default(), Color::Green);
            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
            term.set_text_fg_color_default(tfcd);
            term.set_text_bg_color_default(tbcd);
            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);

            let tfc = term.text_fg_color();
            let tbc = term.text_bg_color();
            assert_eq!(tfc, Color::XtermWhite);
            assert_eq!(tbc, Color::TransparentBg);
            term.append(&format!("Text colors: {sfc} {sbc}\n"));
            term.set_text_fg_color(Color::Green);
            term.set_text_bg_color(Color::DarkBlue);
            assert_eq!(term.text_fg_color(), Color::Green);
            assert_eq!(term.text_bg_color(), Color::DarkBlue);
            term.set_text_fg_color(tfc);
            term.set_text_bg_color(tbc);
            assert_eq!(term.text_fg_color(), Color::XtermWhite);
            assert_eq!(term.text_bg_color(), Color::TransparentBg);

            let tf = term.text_font();
            term.append(&format!("Text font: {tf:?}\n"));
            assert_eq!(tf, Font::Courier);
            term.set_text_font(Font::Screen);
            assert_eq!(term.text_font(), Font::Screen);
            term.set_text_font(tf);
            assert_eq!(term.text_font(), Font::Courier);

            let ts = term.text_size();
            term.append(&format!("Text size: {ts}\n"));
            assert_eq!(ts, 14);
            term.set_text_size(30);
            assert_eq!(term.text_size(), 30);
            term.set_text_size(ts);
            assert_eq!(term.text_size(), ts);

            // Keyboard handler
            term.handle({
                move |term, e| {
                    match e {
                        fltk::enums::Event::KeyDown
                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
                        {
                            // false to let FLTK handle ESC. true to hide ESC
                            false
                        }

                        fltk::enums::Event::KeyDown
                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
                        {
                            // We handle control keystroke
                            let k = fltk::app::event_text();
                            term.append_utf8(&k);
                            true
                        }

                        fltk::enums::Event::KeyDown
                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
                        {
                            // We handle normal printable keystroke
                            let k = fltk::app::event_text();
                            term.take_focus().unwrap();
                            term.append(&k);
                            true
                        }

                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
                        // We can do this, or else ignore them (return false)
                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
                        //     term.redraw();
                        //     true
                        // }
                        _ => false, // Let FLTK handle everything else
                    }
                }
            });

            let attr_save = term.text_attrib();
            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
            term.append("\nStartup tests complete. Keyboard is live.\n");
            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
            term.set_text_attrib(attr_save);
            assert_eq!(term.text_attrib(), attr_save);
            term.redraw();
        }
    });

    app.run().unwrap();
}
source

pub fn do_callback<W: MenuExt>(&mut self, w: &W)

Run the menu item’s callback

source

pub fn emit<T: 'static + Clone + Send + Sync>( &mut self, sender: Sender<T>, msg: T )

Use a sender to send a message during callback

source

pub fn was_deleted(&self) -> bool

Check if a menu item was deleted

source

pub fn draw<M: MenuExt>( &self, x: i32, y: i32, w: i32, h: i32, menu: &M, selected: bool )

Draw a box around the menu item. Requires the call to be made inside a MenuExt-implementing widget’s own draw method

source

pub fn measure(&self) -> (i32, i32)

Measure the width and height of a menu item

source

pub fn add_image<I: ImageExt>(&mut self, image: Option<I>, on_left: bool)

Add an image to a menu item

use fltk::{prelude::*, *};
const PXM: &[&str] = &[
    "13 11 3 1",
    "   c None",
    "x  c #d8d833",
    "@  c #808011",
    "             ",
    "     @@@@    ",
    "    @xxxx@   ",
    "@@@@@xxxx@@  ",
    "@xxxxxxxxx@  ",
    "@xxxxxxxxx@  ",
    "@xxxxxxxxx@  ",
    "@xxxxxxxxx@  ",
    "@xxxxxxxxx@  ",
    "@xxxxxxxxx@  ",
    "@@@@@@@@@@@  "
];
let image = image::Pixmap::new(PXM).unwrap();
let mut menu = menu::MenuBar::default();
menu.add(
    "&File/Open...\t",
    enums::Shortcut::Ctrl | 'o',
    menu::MenuFlag::Normal,
    |_| println!("Opened file!"),
);
if let Some(mut item) = menu.find_item("&File/Open...\t") {
    item.add_image(Some(image), true);
}
source

pub fn add<F: FnMut(&mut Choice) + 'static>( &mut self, name: &str, shortcut: Shortcut, flag: MenuFlag, cb: F ) -> i32

Add a menu item

source

pub fn insert<F: FnMut(&mut Choice) + 'static>( &mut self, idx: i32, name: &str, shortcut: Shortcut, flag: MenuFlag, cb: F ) -> i32

Insert a menu item

source

pub fn add_emit<T: 'static + Clone + Send + Sync>( &mut self, label: &str, shortcut: Shortcut, flag: MenuFlag, sender: Sender<T>, msg: T ) -> i32

Add a menu item along with an emit (sender and message).

source

pub fn insert_emit<T: 'static + Clone + Send + Sync>( &mut self, idx: i32, label: &str, shortcut: Shortcut, flag: MenuFlag, sender: Sender<T>, msg: T ) -> i32

Insert a menu item along with an emit (sender and message).

source

pub fn set_shortcut(&mut self, shortcut: Shortcut)

Set the menu item’s shortcut

Examples found in repository?
examples/terminal.rs (line 53)
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
fn main() {
    let app = fltk::app::App::default();

    // Set panic handler for main thread (will become UI thread)
    std::panic::set_hook(Box::new({
        |e| {
            eprintln!("!!!!PANIC!!!!{:#?}", e);
            error_box(e.to_string()); // Only works from the UI thread
            std::process::exit(2);
        }
    }));

    let mut main_win = Window::new(
        2285,
        180,
        WIN_WIDTH,
        WIN_HEIGHT,
        "FLTK/Terminal Rust wrapper test",
    );
    main_win.set_type(WindowType::Double);
    main_win.make_resizable(true);

    let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);

    let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
    term.set_label("term");
    main_win.resizable(&term);
    term.set_label_type(LabelType::None);

    let idx = menu_bar.add_choice("Test&1");
    menu_bar.at(idx).unwrap().set_callback({
        let mut term1 = term.clone();
        move |c| mb_test1_cb(c, &mut term1)
    });
    menu_bar
        .at(idx)
        .unwrap()
        .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1

    let idx = menu_bar.add_choice("Test&2");
    menu_bar.at(idx).unwrap().set_callback({
        let mut term1 = term.clone();
        move |c| mb_test2_cb(c, &mut term1)
    });
    menu_bar
        .at(idx)
        .unwrap()
        .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2

    let idx = menu_bar.add_choice("Test&3");
    menu_bar.at(idx).unwrap().set_callback({
        let mut term1 = term.clone();
        move |c| mb_test3_cb(c, &mut term1)
    });
    menu_bar
        .at(idx)
        .unwrap()
        .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3

    let idx = menu_bar.add_choice("Test&4");
    menu_bar.at(idx).unwrap().set_callback({
        let mut term1 = term.clone();
        move |c| mb_test4_cb(c, &mut term1)
    });
    menu_bar
        .at(idx)
        .unwrap()
        .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4

    let idx = menu_bar.add_choice("Test&5");
    menu_bar.at(idx).unwrap().set_callback({
        let mut term1 = term.clone();
        move |c| mb_test5_cb(c, &mut term1)
    });
    menu_bar
        .at(idx)
        .unwrap()
        .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5

    menu_bar.end();

    main_win.end();
    main_win.show();

    // Worker thread that drives the startup tests
    let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
        let mut term = term.clone();
        move || {
            println!("Startup tests\n");
            term.append("Startup tests\n\n");

            // Testing ansi() and set_ansi() methods
            assert!(term.ansi(), "Default ANSI mode should be ON at startup");
            term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
            term.set_ansi(false);
            assert!(!term.ansi());
            term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
            // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
            term.append_u8(b"Appending u8 array\n");
            term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
            term.set_ansi(true); // Restore ANSI state

            // Test show_unknown() as incidental part of testing append methods
            term.set_show_unknown(true);
            assert!(term.show_unknown());
            term.append_ascii(
                "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
            );
            term.set_show_unknown(false);
            assert!(!term.show_unknown());

            term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
            term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");

            let r = term.cursor_row();
            assert_eq!(term.cursor_col(), 0);
            term.append(&format!("Testing cursor row/col {r}"));
            assert_eq!(term.cursor_col(), 24);
            assert_eq!(term.cursor_row(), r);

            // Test cursor color methods
            assert_eq!(
                term.cursor_bg_color(),
                Color::XtermGreen,
                "Default cursor bg at startup"
            );
            assert_eq!(
                term.cursor_fg_color(),
                Color::from_hex(0xff_ff_f0),
                "Default cursor fg at startup"
            );
            term.set_cursor_bg_color(Color::Red);
            assert_eq!(term.cursor_bg_color(), Color::Red);
            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
            term.set_cursor_fg_color(Color::Blue);
            assert_eq!(term.cursor_bg_color(), Color::Red);
            assert_eq!(term.cursor_fg_color(), Color::Blue);
            term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
            term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
            assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
            assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));

            // The default display_rows() will derive from the window size
            let dr = term.display_rows();
            assert!(dr > 20, "Default display_rows at startup");
            term.set_display_rows(60);
            assert_eq!(term.display_rows(), 60);
            term.set_display_rows(dr); // Set back to default
            assert_eq!(term.display_rows(), dr);

            // The default display_columns() will derive from the window size
            let dc = term.display_columns();
            assert!(dc > 80, "Default display_rows at startup");
            term.set_display_columns(200);
            assert_eq!(term.display_columns(), 200);
            term.append("\n         1         2         3         4         5         6         7         8         9");
            term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
            term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
            term.set_display_columns(90);
            assert_eq!(term.display_columns(), 90);
            term.set_display_columns(dc); // Set back to default
            assert_eq!(term.display_columns(), dc);

            let hl = term.history_lines();
            assert_eq!(hl, 100, "Default history_lines at startup");
            term.set_history_lines(60);
            assert_eq!(term.history_lines(), 60);
            term.set_history_lines(hl); // Set back to default
            assert_eq!(term.history_lines(), hl);

            // Is history_rows() an alias for history_lines()?
            assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
            term.set_history_rows(50);
            assert_eq!(term.history_rows(), 50);
            term.set_history_lines(100); // Set back to default
            assert_eq!(term.history_lines(), 100);

            let hu = term.history_use();
            term.append(&format!(
                "history_use = {hu} (it's not clear what this means)\n"
            ));
            // assert_eq!(term.history_use(), hu+1);

            term.append(&format!(
                "margins = b:{} l:{} r:{} t{}\n",
                term.margin_bottom(),
                term.margin_left(),
                term.margin_right(),
                term.margin_top()
            ));
            assert_eq!(term.margin_bottom(), 3);
            assert_eq!(term.margin_left(), 3);
            assert_eq!(term.margin_right(), 3);
            assert_eq!(term.margin_top(), 3);

            term.set_margin_bottom(5);
            term.set_margin_left(10);
            term.set_margin_right(15);
            term.set_margin_top(20);
            assert_eq!(term.margin_bottom(), 5);
            assert_eq!(term.margin_left(), 10);
            assert_eq!(term.margin_right(), 15);
            assert_eq!(term.margin_top(), 20);

            term.append("Single character: '");
            term.print_char('X');
            term.append("', single UTF-8 character: '");
            term.print_char_utf8('↑');
            term.append("'\n");

            let rr = term.redraw_rate();
            assert_eq!(rr, 0.1, "Default redraw rate at startup");
            term.append(&format!("Redraw rate {rr}\n"));
            term.set_redraw_rate(1.0);
            assert_eq!(term.redraw_rate(), 1.0);
            term.set_redraw_rate(rr);
            assert_eq!(term.redraw_rate(), rr);

            let rs = term.redraw_style();
            term.append(&format!("Redraw style {rs:?}\n"));
            assert_eq!(
                rs,
                RedrawStyle::RateLimited,
                "Default redraw style at startup"
            );
            term.set_redraw_style(RedrawStyle::NoRedraw);
            assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
            term.set_redraw_style(rs);
            assert_eq!(term.redraw_style(), rs);

            // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
            assert_eq!(
                RedrawStyle::NoRedraw.bits(),
                0x0000,
                "RedrawStyle enum values have been reassigned"
            );
            assert_eq!(
                RedrawStyle::RateLimited.bits(),
                0x0001,
                "RedrawStyle enum values have been reassigned"
            );
            assert_eq!(
                RedrawStyle::PerWrite.bits(),
                0x0002,
                "RedrawStyle enum values have been reassigned"
            );

            term.append(&format!(
                "Scrollbar actual size {}\n",
                term.scrollbar_actual_size()
            ));
            assert_eq!(term.scrollbar_actual_size(), 16);
            term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
            assert_eq!(
                term.scrollbar_size(),
                0,
                "Default scrollbar size at startup"
            );
            term.set_scrollbar_size(40);
            assert_eq!(term.scrollbar_size(), 40);
            assert_eq!(term.scrollbar_actual_size(), 40);
            term.append(&format!(
                "Scrollbar actual size {}\n",
                term.scrollbar_actual_size()
            ));
            term.set_scrollbar_size(0); // Restore default
            assert_eq!(term.scrollbar_size(), 0);
            assert_eq!(term.scrollbar_actual_size(), 16);

            let sfc = term.selection_fg_color();
            let sbc = term.selection_bg_color();
            assert_eq!(sfc, Color::Black);
            assert_eq!(sbc, Color::White);
            term.append(&format!("Selection colors: {sfc} {sbc}\n"));
            term.set_selection_fg_color(Color::Green);
            term.set_selection_bg_color(Color::DarkBlue);
            assert_eq!(term.selection_fg_color(), Color::Green);
            assert_eq!(term.selection_bg_color(), Color::DarkBlue);
            term.set_selection_fg_color(sfc);
            term.set_selection_bg_color(sbc);
            assert_eq!(term.selection_fg_color(), Color::Black);
            assert_eq!(term.selection_bg_color(), Color::White);

            let tfcd = term.text_fg_color_default();
            let tbcd = term.text_bg_color_default();
            assert_eq!(tfcd, Color::XtermWhite);
            assert_eq!(tbcd, Color::TransparentBg);
            term.append(&format!("Default text colors: {sfc} {sbc}\n"));
            term.set_text_fg_color_default(Color::Green);
            term.set_text_bg_color_default(Color::DarkBlue);
            assert_eq!(term.text_fg_color_default(), Color::Green);
            assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
            term.set_text_fg_color_default(tfcd);
            term.set_text_bg_color_default(tbcd);
            assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
            assert_eq!(term.text_bg_color_default(), Color::TransparentBg);

            let tfc = term.text_fg_color();
            let tbc = term.text_bg_color();
            assert_eq!(tfc, Color::XtermWhite);
            assert_eq!(tbc, Color::TransparentBg);
            term.append(&format!("Text colors: {sfc} {sbc}\n"));
            term.set_text_fg_color(Color::Green);
            term.set_text_bg_color(Color::DarkBlue);
            assert_eq!(term.text_fg_color(), Color::Green);
            assert_eq!(term.text_bg_color(), Color::DarkBlue);
            term.set_text_fg_color(tfc);
            term.set_text_bg_color(tbc);
            assert_eq!(term.text_fg_color(), Color::XtermWhite);
            assert_eq!(term.text_bg_color(), Color::TransparentBg);

            let tf = term.text_font();
            term.append(&format!("Text font: {tf:?}\n"));
            assert_eq!(tf, Font::Courier);
            term.set_text_font(Font::Screen);
            assert_eq!(term.text_font(), Font::Screen);
            term.set_text_font(tf);
            assert_eq!(term.text_font(), Font::Courier);

            let ts = term.text_size();
            term.append(&format!("Text size: {ts}\n"));
            assert_eq!(ts, 14);
            term.set_text_size(30);
            assert_eq!(term.text_size(), 30);
            term.set_text_size(ts);
            assert_eq!(term.text_size(), ts);

            // Keyboard handler
            term.handle({
                move |term, e| {
                    match e {
                        fltk::enums::Event::KeyDown
                            if fltk::app::event_key() == fltk::enums::Key::Escape =>
                        {
                            // false to let FLTK handle ESC. true to hide ESC
                            false
                        }

                        fltk::enums::Event::KeyDown
                            if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
                        {
                            // We handle control keystroke
                            let k = fltk::app::event_text();
                            term.append_utf8(&k);
                            true
                        }

                        fltk::enums::Event::KeyDown
                            if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
                        {
                            // We handle normal printable keystroke
                            let k = fltk::app::event_text();
                            term.take_focus().unwrap();
                            term.append(&k);
                            true
                        }

                        // fltk docs say that keyboard handler should always claim Focus and Unfocus events
                        // We can do this, or else ignore them (return false)
                        // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
                        //     term.redraw();
                        //     true
                        // }
                        _ => false, // Let FLTK handle everything else
                    }
                }
            });

            let attr_save = term.text_attrib();
            term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
            term.append("\nStartup tests complete. Keyboard is live.\n");
            assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
            term.set_text_attrib(attr_save);
            assert_eq!(term.text_attrib(), attr_save);
            term.redraw();
        }
    });

    app.run().unwrap();
}
source

pub fn set_flag(&mut self, flag: MenuFlag)

Set the menu item’s shortcut

Trait Implementations§

source§

impl Clone for MenuItem

source§

fn clone(&self) -> MenuItem

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for MenuItem

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Drop for MenuItem

source§

fn drop(&mut self)

Executes the destructor for this type. Read more
source§

impl IntoIterator for MenuItem

§

type Item = MenuItem

The type of the elements being iterated over.
§

type IntoIter = IntoIter<<MenuItem as IntoIterator>::Item>

Which kind of iterator are we turning this into?
source§

fn into_iter(self) -> Self::IntoIter

Creates an iterator from a value. Read more
source§

impl PartialEq for MenuItem

source§

fn eq(&self, other: &Self) -> bool

This method tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
source§

impl Eq for MenuItem

source§

impl Send for MenuItem

Available on non-crate feature single-threaded only.
source§

impl Sync for MenuItem

Available on non-crate feature single-threaded only.

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for T
where U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T> ToOwned for T
where T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.