pub struct MenuItem { /* private fields */ }
Expand description
Creates a menu item
Implementations§
Source§impl MenuItem
impl MenuItem
Sourcepub unsafe fn from_ptr(ptr: *mut Fl_Menu_Item) -> MenuItem
pub unsafe fn from_ptr(ptr: *mut Fl_Menu_Item) -> MenuItem
Sourcepub unsafe fn as_ptr(&self) -> *mut Fl_Menu_Item
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
Sourcepub fn new(choices: &[&'static str]) -> MenuItem
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?
5fn main() {
6 let app = app::App::default().with_scheme(app::Scheme::Gtk);
7 app::background(211, 211, 211);
8
9 let mut win = window::Window::default().with_size(900, 300);
10 let mut b = browser::HoldBrowser::default()
11 .with_size(900 - 10, 300 - 10)
12 .center_of(&win);
13 let widths = &[50, 50, 50, 70, 70, 40, 40, 70, 70, 50];
14
15 b.set_column_widths(widths);
16 b.set_column_char('\t');
17 b.add("USER\tPID\t%CPU\t%MEM\tVSZ\tRSS\tTTY\tSTAT\tSTART\tTIME\tCOMMAND");
18 b.add("root\t2888\t0.0\t0.0\t1352\t0\ttty3\tSW\tAug15\t0:00\t@b@f/sbin/mingetty tty3");
19 b.add("erco\t2889\t0.0\t13.0\t221352\t0\ttty3\tR\tAug15\t1:34\t@b@f/usr/local/bin/render a35 0004");
20 b.add("uucp\t2892\t0.0\t0.0\t1352\t0\tttyS0\tSW\tAug15\t0:00\t@b@f/sbin/agetty -h 19200 ttyS0 vt100");
21 b.add("root\t13115\t0.0\t0.0\t1352\t0\ttty2\tSW\tAug30\t0:00\t@b@f/sbin/mingetty tty2");
22 b.add(
23 "root\t13464\t0.0\t0.0\t1352\t0\ttty1\tSW\tAug30\t0:00\t@b@f/sbin/mingetty tty1 --noclear",
24 );
25
26 let menu = menu::MenuItem::new(&["1st menu item\t", "2nd menu item\t", "3rd menu item\t"]);
27 b.select(2);
28
29 b.set_callback(move |_| {
30 if app::event_mouse_button() == app::MouseButton::Right {
31 // or app::event_button() == 3
32 let coords = app::event_coords();
33 match menu.popup(coords.0, coords.1) {
34 None => println!("No value was chosen!"),
35 Some(val) => println!("{}", val.label().unwrap()),
36 }
37 }
38 });
39
40 win.make_resizable(true);
41 win.end();
42 win.show();
43 app.run().unwrap();
44}
Sourcepub fn popup(&self, x: i32, y: i32) -> Option<MenuItem>
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?
5fn main() {
6 let app = app::App::default().with_scheme(app::Scheme::Gtk);
7 app::background(211, 211, 211);
8
9 let mut win = window::Window::default().with_size(900, 300);
10 let mut b = browser::HoldBrowser::default()
11 .with_size(900 - 10, 300 - 10)
12 .center_of(&win);
13 let widths = &[50, 50, 50, 70, 70, 40, 40, 70, 70, 50];
14
15 b.set_column_widths(widths);
16 b.set_column_char('\t');
17 b.add("USER\tPID\t%CPU\t%MEM\tVSZ\tRSS\tTTY\tSTAT\tSTART\tTIME\tCOMMAND");
18 b.add("root\t2888\t0.0\t0.0\t1352\t0\ttty3\tSW\tAug15\t0:00\t@b@f/sbin/mingetty tty3");
19 b.add("erco\t2889\t0.0\t13.0\t221352\t0\ttty3\tR\tAug15\t1:34\t@b@f/usr/local/bin/render a35 0004");
20 b.add("uucp\t2892\t0.0\t0.0\t1352\t0\tttyS0\tSW\tAug15\t0:00\t@b@f/sbin/agetty -h 19200 ttyS0 vt100");
21 b.add("root\t13115\t0.0\t0.0\t1352\t0\ttty2\tSW\tAug30\t0:00\t@b@f/sbin/mingetty tty2");
22 b.add(
23 "root\t13464\t0.0\t0.0\t1352\t0\ttty1\tSW\tAug30\t0:00\t@b@f/sbin/mingetty tty1 --noclear",
24 );
25
26 let menu = menu::MenuItem::new(&["1st menu item\t", "2nd menu item\t", "3rd menu item\t"]);
27 b.select(2);
28
29 b.set_callback(move |_| {
30 if app::event_mouse_button() == app::MouseButton::Right {
31 // or app::event_button() == 3
32 let coords = app::event_coords();
33 match menu.popup(coords.0, coords.1) {
34 None => println!("No value was chosen!"),
35 Some(val) => println!("{}", val.label().unwrap()),
36 }
37 }
38 });
39
40 win.make_resizable(true);
41 win.end();
42 win.show();
43 app.run().unwrap();
44}
Sourcepub fn pulldown(
&self,
x: i32,
y: i32,
w: i32,
h: i32,
picked: Option<MenuItem>,
menu: Option<&impl MenuExt>,
) -> Option<MenuItem>
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
Sourcepub fn label(&self) -> Option<String>
pub fn label(&self) -> Option<String>
Returns the label of the menu item
Examples found in repository?
5fn main() {
6 let app = app::App::default().with_scheme(app::Scheme::Gtk);
7 app::background(211, 211, 211);
8
9 let mut win = window::Window::default().with_size(900, 300);
10 let mut b = browser::HoldBrowser::default()
11 .with_size(900 - 10, 300 - 10)
12 .center_of(&win);
13 let widths = &[50, 50, 50, 70, 70, 40, 40, 70, 70, 50];
14
15 b.set_column_widths(widths);
16 b.set_column_char('\t');
17 b.add("USER\tPID\t%CPU\t%MEM\tVSZ\tRSS\tTTY\tSTAT\tSTART\tTIME\tCOMMAND");
18 b.add("root\t2888\t0.0\t0.0\t1352\t0\ttty3\tSW\tAug15\t0:00\t@b@f/sbin/mingetty tty3");
19 b.add("erco\t2889\t0.0\t13.0\t221352\t0\ttty3\tR\tAug15\t1:34\t@b@f/usr/local/bin/render a35 0004");
20 b.add("uucp\t2892\t0.0\t0.0\t1352\t0\tttyS0\tSW\tAug15\t0:00\t@b@f/sbin/agetty -h 19200 ttyS0 vt100");
21 b.add("root\t13115\t0.0\t0.0\t1352\t0\ttty2\tSW\tAug30\t0:00\t@b@f/sbin/mingetty tty2");
22 b.add(
23 "root\t13464\t0.0\t0.0\t1352\t0\ttty1\tSW\tAug30\t0:00\t@b@f/sbin/mingetty tty1 --noclear",
24 );
25
26 let menu = menu::MenuItem::new(&["1st menu item\t", "2nd menu item\t", "3rd menu item\t"]);
27 b.select(2);
28
29 b.set_callback(move |_| {
30 if app::event_mouse_button() == app::MouseButton::Right {
31 // or app::event_button() == 3
32 let coords = app::event_coords();
33 match menu.popup(coords.0, coords.1) {
34 None => println!("No value was chosen!"),
35 Some(val) => println!("{}", val.label().unwrap()),
36 }
37 }
38 });
39
40 win.make_resizable(true);
41 win.end();
42 win.show();
43 app.run().unwrap();
44}
More examples
122fn main() {
123 let style = Rc::from(RefCell::from(Style::new()));
124
125 let app = App::default().with_scheme(Scheme::Gleam);
126 let mut wind = Window::default()
127 .with_size(500, 200)
128 .with_label("Highlight");
129 let mut vpack = Pack::new(4, 4, 492, 192, "");
130 vpack.set_spacing(4);
131 let mut text_editor = TextEditor::default().with_size(492, 163);
132
133 let mut hpack = Pack::new(4, 4, 492, 25, "").with_type(PackType::Horizontal);
134 hpack.set_spacing(8);
135 let mut font = Choice::default().with_size(130, 25);
136 let mut choice = Choice::default().with_size(130, 25);
137 let mut size = Spinner::default().with_size(60, 25);
138
139 let mut color = Choice::default().with_size(100, 25);
140 let mut btn_clear = Button::default().with_size(40, 25).with_label("X");
141 hpack.end();
142
143 vpack.end();
144 wind.end();
145 wind.show();
146
147 text_editor.wrap_mode(fltk::text::WrapMode::AtBounds, 0);
148 text_editor.set_buffer(TextBuffer::default());
149
150 font.add_choice("Courier|Helvetica|Times");
151 font.set_value(0);
152 font.set_tooltip("Font");
153
154 choice.add_choice("Normal|Underline|Strike");
155 choice.set_value(0);
156
157 size.set_value(18.0);
158 size.set_step(1.0);
159 size.set_range(12.0, 28.0);
160 size.set_tooltip("Size");
161
162 color.set_tooltip("Color");
163 color.add_choice("#000000|#ff0000|#00ff00|#0000ff|#ffff00|#00ffff");
164 color.set_value(0);
165
166 btn_clear.set_label_color(Color::Red);
167 btn_clear.set_tooltip("Clear style");
168
169 // set colors
170 for mut item in color.clone() {
171 if let Some(lbl) = item.label() {
172 item.set_label_color(Color::from_u32(
173 u32::from_str_radix(lbl.trim().strip_prefix('#').unwrap(), 16)
174 .ok()
175 .unwrap(),
176 ));
177 }
178 }
179
180 let style_rc1 = Rc::clone(&style);
181
182 text_editor.buffer().unwrap().add_modify_callback({
183 let mut text_editor1 = text_editor.clone();
184 let font1 = font.clone();
185 let size1 = size.clone();
186 let color1 = color.clone();
187 let choice1 = choice.clone();
188 move |pos: i32, ins_items: i32, del_items: i32, _: i32, _: &str| {
189 let attr = if choice1.value() == 1 {
190 TextAttr::Underline
191 } else if choice1.value() == 2 {
192 TextAttr::StrikeThrough
193 } else {
194 TextAttr::None
195 };
196 if ins_items > 0 || del_items > 0 {
197 let mut style = style_rc1.borrow_mut();
198 let color = Color::from_u32(
199 u32::from_str_radix(
200 color1
201 .text(color1.value())
202 .unwrap()
203 .trim()
204 .strip_prefix('#')
205 .unwrap(),
206 16,
207 )
208 .ok()
209 .unwrap(),
210 );
211 style.apply_style(
212 Some(pos),
213 Some(ins_items),
214 Some(del_items),
215 None,
216 None,
217 Font::by_name(font1.text(font1.value()).unwrap().trim()),
218 size1.value() as i32,
219 color,
220 attr,
221 &mut text_editor1,
222 );
223 }
224 }
225 });
226
227 color.set_callback({
228 let size = size.clone();
229 let font = font.clone();
230 let choice = choice.clone();
231 let mut text_editor = text_editor.clone();
232 let style_rc1 = Rc::clone(&style);
233 move |color| {
234 let attr = match choice.value() {
235 0 => TextAttr::None,
236 1 => TextAttr::Underline,
237 2 => TextAttr::StrikeThrough,
238 _ => unreachable!(),
239 };
240 if let Some(buf) = text_editor.buffer() {
241 if let Some((s, e)) = buf.selection_position() {
242 let mut style = style_rc1.borrow_mut();
243 let color = Color::from_u32(
244 u32::from_str_radix(
245 color
246 .text(color.value())
247 .unwrap()
248 .trim()
249 .strip_prefix('#')
250 .unwrap(),
251 16,
252 )
253 .ok()
254 .unwrap(),
255 );
256 style.apply_style(
257 None,
258 None,
259 None,
260 Some(s),
261 Some(e),
262 Font::by_name(font.text(font.value()).unwrap().trim()),
263 size.value() as i32,
264 color,
265 attr,
266 &mut text_editor,
267 );
268 }
269 }
270 }
271 });
272
273 // get the style from the current cursor position
274 text_editor.handle({
275 let style_rc1 = Rc::clone(&style);
276 let mut font1 = font.clone();
277 let mut size1 = size.clone();
278 let mut color1 = color.clone();
279 move |te, e| match e {
280 Event::KeyUp | Event::Released => {
281 if let Some(buff) = te.style_buffer() {
282 let i = te.insert_position();
283 if let Some(t) = buff.text_range(i, i + 1) {
284 if !t.is_empty() {
285 let style = style_rc1.borrow_mut();
286 if let Some(i) = t.chars().next().map(|c| (c as usize - 65)) {
287 if let Some(style) = style.style_table.get(i) {
288 if let Some(mn) = font1.find_item(&format!("{:?}", style.font))
289 {
290 font1.set_item(&mn);
291 }
292 size1.set_value(style.size as f64);
293 let (r, g, b) = style.color.to_rgb();
294 if let Some(mn) =
295 color1.find_item(format!("{r:02x}{g:02x}{b:02x}").as_str())
296 {
297 color1.set_item(&mn);
298 }
299 }
300 }
301 }
302 }
303 }
304 true
305 }
306 _ => false,
307 }
308 });
309
310 choice.set_callback({
311 let mut color1 = color.clone();
312 move |_| color1.do_callback()
313 });
314
315 font.set_callback({
316 let mut color1 = color.clone();
317 move |_| color1.do_callback()
318 });
319
320 size.set_callback({
321 let mut color1 = color.clone();
322 move |_| color1.do_callback()
323 });
324
325 // clear style of the current selection or, if no text is selected, clear all text style
326 btn_clear.set_callback({
327 let style_rc1 = Rc::clone(&style);
328 let text_editor1 = text_editor.clone();
329 move |_| {
330 match text_editor1.buffer().unwrap().selection_position() {
331 Some((_, _)) => {
332 font.set_value(0);
333 size.set_value(18.0);
334 color.set_value(0);
335 choice.set_value(0);
336 color.do_callback();
337 }
338 None => {
339 font.set_value(0);
340 size.set_value(18.0);
341 color.set_value(0);
342 style_rc1.borrow_mut().apply_style(
343 None,
344 None,
345 None,
346 Some(0),
347 Some(text_editor1.buffer().unwrap().length()),
348 Font::Courier,
349 16,
350 Color::Black,
351 TextAttr::None,
352 &mut text_editor,
353 );
354 }
355 };
356 }
357 });
358
359 app.run().unwrap();
360}
Sourcepub fn label_type(&self) -> LabelType
pub fn label_type(&self) -> LabelType
Returns the label type of the menu item
Sourcepub fn set_label_type(&mut self, typ: LabelType)
pub fn set_label_type(&mut self, typ: LabelType)
Sets the label type of the menu item
Sourcepub fn label_color(&self) -> Color
pub fn label_color(&self) -> Color
Returns the label color of the menu item
Sourcepub fn set_label_color(&mut self, color: Color)
pub fn set_label_color(&mut self, color: Color)
Sets the label color of the menu item
Examples found in repository?
324 pub fn save_file(&mut self) -> Result<bool, Box<dyn error::Error>> {
325 match &self.filename {
326 Some(f) => {
327 self.buf.save_file(f)?;
328 self.modified = false;
329 self.menu
330 .menu
331 .find_item("&File/&Save\t")
332 .unwrap()
333 .deactivate();
334 self.menu
335 .menu
336 .find_item("&File/&Quit\t")
337 .unwrap()
338 .set_label_color(Color::Black);
339 let name = match &self.filename {
340 Some(f) => f.to_string_lossy().to_string(),
341 None => "(Untitled)".to_string(),
342 };
343 self.main_win.set_label(&format!("{name} - RustyEd"));
344 Ok(true)
345 }
346 None => self.save_file_as(),
347 }
348 }
349
350 /** Called by "Save As..." or by "Save" in case no file was set yet.
351 * Returns true if the file was succesfully saved. */
352 pub fn save_file_as(&mut self) -> Result<bool, Box<dyn error::Error>> {
353 if let Some(c) = nfc_get_file(dialog::NativeFileChooserType::BrowseSaveFile) {
354 self.buf.save_file(&c)?;
355 self.modified = false;
356 self.menu
357 .menu
358 .find_item("&File/&Save\t")
359 .unwrap()
360 .deactivate();
361 self.menu
362 .menu
363 .find_item("&File/&Quit\t")
364 .unwrap()
365 .set_label_color(Color::Black);
366 self.filename = Some(c);
367 self.main_win
368 .set_label(&format!("{:?} - RustyEd", self.filename.as_ref().unwrap()));
369 Ok(true)
370 } else {
371 Ok(false)
372 }
373 }
374
375 pub fn launch(&mut self) {
376 while self.app.wait() {
377 use Message::*;
378 if let Some(msg) = self.r.recv() {
379 match msg {
380 Changed => {
381 if !self.modified {
382 self.modified = true;
383 self.menu.menu.find_item("&File/&Save\t").unwrap().activate();
384 self.menu.menu.find_item("&File/&Quit\t").unwrap().set_label_color(Color::Red);
385 let name = match &self.filename {
386 Some(f) => f.to_string_lossy().to_string(),
387 None => "(Untitled)".to_string(),
388 };
389 self.main_win.set_label(&format!("* {name} - RustyEd"));
390 }
391 }
392 New => {
393 if self.buf.text() != "" {
394 let clear = if let Some(x) = dialog::choice2(center().0 - 200, center().1 - 100, "File unsaved, Do you wish to continue?", "&Yes", "&No!", "") {
395 x == 0
396 } else {
397 false
398 };
399 if clear {
400 self.buf.set_text("");
401 }
402 }
403 },
404 Open => {
405 if let Some(c) = nfc_get_file(dialog::NativeFileChooserType::BrowseFile) {
406 if c.exists() {
407 match self.buf.load_file(&c) {
408 Ok(_) => self.filename = Some(c),
409 Err(e) => dialog::alert(center().0 - 200, center().1 - 100, &format!("An issue occured while loading the file: {e}")),
410 }
411 } else {
412 dialog::alert(center().0 - 200, center().1 - 100, "File does not exist!")
413 }
414 }
415 },
416 Save => { self.save_file().unwrap(); },
417 SaveAs => { self.save_file_as().unwrap(); },
418 Print => {
419 let mut printer = printer::Printer::default();
420 if printer.begin_job(0).is_ok() {
421 let (w, h) = printer.printable_rect();
422 self.printable.set_size(w - 40, h - 40);
423 // Needs cleanup
424 let line_count = self.printable.count_lines(0, self.printable.buffer().unwrap().length(), true) / 45;
425 for i in 0..=line_count {
426 self.printable.scroll(45 * i, 0);
427 printer.begin_page().ok();
428 printer.print_widget(&self.printable, 20, 20);
429 printer.end_page().ok();
430 }
431 printer.end_job();
432 }
433 },
434 Quit => {
435 if self.modified {
436 match dialog::choice2(center().0 - 200, center().1 - 100,
437 "Would you like to save your work?", "&Yes", "&No", "") {
438 Some(0) => {
439 if self.save_file().unwrap() {
440 self.app.quit();
441 }
442 },
443 Some(1) => { self.app.quit() },
444 Some(_) | None => (),
445 }
446 } else {
447 self.app.quit();
448 }
449 },
450 Cut => self.editor.cut(),
451 Copy => self.editor.copy(),
452 Paste => self.editor.paste(),
453 About => dialog::message(center().0 - 300, center().1 - 100, "This is an example application written in Rust and using the FLTK Gui library."),
454 }
455 }
456 }
457 }
More examples
24fn init_menu(m: &mut menu::SysMenuBar) {
25 m.add(
26 "&File/&New...\t",
27 Shortcut::Ctrl | 'n',
28 menu::MenuFlag::Normal,
29 menu_cb,
30 );
31 m.add(
32 "&File/&Open...\t",
33 Shortcut::Ctrl | 'o',
34 menu::MenuFlag::Normal,
35 menu_cb,
36 );
37 m.add(
38 "&File/&Save\t",
39 Shortcut::Ctrl | 's',
40 menu::MenuFlag::Normal,
41 menu_cb,
42 );
43 m.add(
44 "&File/Save &as...\t",
45 Shortcut::Ctrl | 'w',
46 menu::MenuFlag::MenuDivider,
47 menu_cb,
48 );
49 let idx = m.add(
50 "&File/&Quit\t",
51 Shortcut::Ctrl | 'q',
52 menu::MenuFlag::Normal,
53 menu_cb,
54 );
55 m.at(idx).unwrap().set_label_color(Color::Red);
56 m.add(
57 "&Edit/Cu&t\t",
58 Shortcut::Ctrl | 'x',
59 menu::MenuFlag::Normal,
60 menu_cb,
61 );
62 m.add(
63 "&Edit/&Copy\t",
64 Shortcut::Ctrl | 'c',
65 menu::MenuFlag::Normal,
66 menu_cb,
67 );
68 m.add(
69 "&Edit/&Paste\t",
70 Shortcut::Ctrl | 'v',
71 menu::MenuFlag::Normal,
72 menu_cb,
73 );
74 m.add(
75 "&Help/&About\t",
76 Shortcut::None,
77 menu::MenuFlag::Normal,
78 menu_cb,
79 );
80}
122fn main() {
123 let style = Rc::from(RefCell::from(Style::new()));
124
125 let app = App::default().with_scheme(Scheme::Gleam);
126 let mut wind = Window::default()
127 .with_size(500, 200)
128 .with_label("Highlight");
129 let mut vpack = Pack::new(4, 4, 492, 192, "");
130 vpack.set_spacing(4);
131 let mut text_editor = TextEditor::default().with_size(492, 163);
132
133 let mut hpack = Pack::new(4, 4, 492, 25, "").with_type(PackType::Horizontal);
134 hpack.set_spacing(8);
135 let mut font = Choice::default().with_size(130, 25);
136 let mut choice = Choice::default().with_size(130, 25);
137 let mut size = Spinner::default().with_size(60, 25);
138
139 let mut color = Choice::default().with_size(100, 25);
140 let mut btn_clear = Button::default().with_size(40, 25).with_label("X");
141 hpack.end();
142
143 vpack.end();
144 wind.end();
145 wind.show();
146
147 text_editor.wrap_mode(fltk::text::WrapMode::AtBounds, 0);
148 text_editor.set_buffer(TextBuffer::default());
149
150 font.add_choice("Courier|Helvetica|Times");
151 font.set_value(0);
152 font.set_tooltip("Font");
153
154 choice.add_choice("Normal|Underline|Strike");
155 choice.set_value(0);
156
157 size.set_value(18.0);
158 size.set_step(1.0);
159 size.set_range(12.0, 28.0);
160 size.set_tooltip("Size");
161
162 color.set_tooltip("Color");
163 color.add_choice("#000000|#ff0000|#00ff00|#0000ff|#ffff00|#00ffff");
164 color.set_value(0);
165
166 btn_clear.set_label_color(Color::Red);
167 btn_clear.set_tooltip("Clear style");
168
169 // set colors
170 for mut item in color.clone() {
171 if let Some(lbl) = item.label() {
172 item.set_label_color(Color::from_u32(
173 u32::from_str_radix(lbl.trim().strip_prefix('#').unwrap(), 16)
174 .ok()
175 .unwrap(),
176 ));
177 }
178 }
179
180 let style_rc1 = Rc::clone(&style);
181
182 text_editor.buffer().unwrap().add_modify_callback({
183 let mut text_editor1 = text_editor.clone();
184 let font1 = font.clone();
185 let size1 = size.clone();
186 let color1 = color.clone();
187 let choice1 = choice.clone();
188 move |pos: i32, ins_items: i32, del_items: i32, _: i32, _: &str| {
189 let attr = if choice1.value() == 1 {
190 TextAttr::Underline
191 } else if choice1.value() == 2 {
192 TextAttr::StrikeThrough
193 } else {
194 TextAttr::None
195 };
196 if ins_items > 0 || del_items > 0 {
197 let mut style = style_rc1.borrow_mut();
198 let color = Color::from_u32(
199 u32::from_str_radix(
200 color1
201 .text(color1.value())
202 .unwrap()
203 .trim()
204 .strip_prefix('#')
205 .unwrap(),
206 16,
207 )
208 .ok()
209 .unwrap(),
210 );
211 style.apply_style(
212 Some(pos),
213 Some(ins_items),
214 Some(del_items),
215 None,
216 None,
217 Font::by_name(font1.text(font1.value()).unwrap().trim()),
218 size1.value() as i32,
219 color,
220 attr,
221 &mut text_editor1,
222 );
223 }
224 }
225 });
226
227 color.set_callback({
228 let size = size.clone();
229 let font = font.clone();
230 let choice = choice.clone();
231 let mut text_editor = text_editor.clone();
232 let style_rc1 = Rc::clone(&style);
233 move |color| {
234 let attr = match choice.value() {
235 0 => TextAttr::None,
236 1 => TextAttr::Underline,
237 2 => TextAttr::StrikeThrough,
238 _ => unreachable!(),
239 };
240 if let Some(buf) = text_editor.buffer() {
241 if let Some((s, e)) = buf.selection_position() {
242 let mut style = style_rc1.borrow_mut();
243 let color = Color::from_u32(
244 u32::from_str_radix(
245 color
246 .text(color.value())
247 .unwrap()
248 .trim()
249 .strip_prefix('#')
250 .unwrap(),
251 16,
252 )
253 .ok()
254 .unwrap(),
255 );
256 style.apply_style(
257 None,
258 None,
259 None,
260 Some(s),
261 Some(e),
262 Font::by_name(font.text(font.value()).unwrap().trim()),
263 size.value() as i32,
264 color,
265 attr,
266 &mut text_editor,
267 );
268 }
269 }
270 }
271 });
272
273 // get the style from the current cursor position
274 text_editor.handle({
275 let style_rc1 = Rc::clone(&style);
276 let mut font1 = font.clone();
277 let mut size1 = size.clone();
278 let mut color1 = color.clone();
279 move |te, e| match e {
280 Event::KeyUp | Event::Released => {
281 if let Some(buff) = te.style_buffer() {
282 let i = te.insert_position();
283 if let Some(t) = buff.text_range(i, i + 1) {
284 if !t.is_empty() {
285 let style = style_rc1.borrow_mut();
286 if let Some(i) = t.chars().next().map(|c| (c as usize - 65)) {
287 if let Some(style) = style.style_table.get(i) {
288 if let Some(mn) = font1.find_item(&format!("{:?}", style.font))
289 {
290 font1.set_item(&mn);
291 }
292 size1.set_value(style.size as f64);
293 let (r, g, b) = style.color.to_rgb();
294 if let Some(mn) =
295 color1.find_item(format!("{r:02x}{g:02x}{b:02x}").as_str())
296 {
297 color1.set_item(&mn);
298 }
299 }
300 }
301 }
302 }
303 }
304 true
305 }
306 _ => false,
307 }
308 });
309
310 choice.set_callback({
311 let mut color1 = color.clone();
312 move |_| color1.do_callback()
313 });
314
315 font.set_callback({
316 let mut color1 = color.clone();
317 move |_| color1.do_callback()
318 });
319
320 size.set_callback({
321 let mut color1 = color.clone();
322 move |_| color1.do_callback()
323 });
324
325 // clear style of the current selection or, if no text is selected, clear all text style
326 btn_clear.set_callback({
327 let style_rc1 = Rc::clone(&style);
328 let text_editor1 = text_editor.clone();
329 move |_| {
330 match text_editor1.buffer().unwrap().selection_position() {
331 Some((_, _)) => {
332 font.set_value(0);
333 size.set_value(18.0);
334 color.set_value(0);
335 choice.set_value(0);
336 color.do_callback();
337 }
338 None => {
339 font.set_value(0);
340 size.set_value(18.0);
341 color.set_value(0);
342 style_rc1.borrow_mut().apply_style(
343 None,
344 None,
345 None,
346 Some(0),
347 Some(text_editor1.buffer().unwrap().length()),
348 Font::Courier,
349 16,
350 Color::Black,
351 TextAttr::None,
352 &mut text_editor,
353 );
354 }
355 };
356 }
357 });
358
359 app.run().unwrap();
360}
Sourcepub fn label_font(&self) -> Font
pub fn label_font(&self) -> Font
Returns the label font of the menu item
Sourcepub fn set_label_font(&mut self, font: Font)
pub fn set_label_font(&mut self, font: Font)
Sets the label font of the menu item
Sourcepub fn label_size(&self) -> i32
pub fn label_size(&self) -> i32
Returns the label size of the menu item
Sourcepub fn set_label_size(&mut self, sz: i32)
pub fn set_label_size(&mut self, sz: i32)
Sets the label size of the menu item
Sourcepub fn activate(&mut self)
pub fn activate(&mut self)
Activates the menu item
Examples found in repository?
375 pub fn launch(&mut self) {
376 while self.app.wait() {
377 use Message::*;
378 if let Some(msg) = self.r.recv() {
379 match msg {
380 Changed => {
381 if !self.modified {
382 self.modified = true;
383 self.menu.menu.find_item("&File/&Save\t").unwrap().activate();
384 self.menu.menu.find_item("&File/&Quit\t").unwrap().set_label_color(Color::Red);
385 let name = match &self.filename {
386 Some(f) => f.to_string_lossy().to_string(),
387 None => "(Untitled)".to_string(),
388 };
389 self.main_win.set_label(&format!("* {name} - RustyEd"));
390 }
391 }
392 New => {
393 if self.buf.text() != "" {
394 let clear = if let Some(x) = dialog::choice2(center().0 - 200, center().1 - 100, "File unsaved, Do you wish to continue?", "&Yes", "&No!", "") {
395 x == 0
396 } else {
397 false
398 };
399 if clear {
400 self.buf.set_text("");
401 }
402 }
403 },
404 Open => {
405 if let Some(c) = nfc_get_file(dialog::NativeFileChooserType::BrowseFile) {
406 if c.exists() {
407 match self.buf.load_file(&c) {
408 Ok(_) => self.filename = Some(c),
409 Err(e) => dialog::alert(center().0 - 200, center().1 - 100, &format!("An issue occured while loading the file: {e}")),
410 }
411 } else {
412 dialog::alert(center().0 - 200, center().1 - 100, "File does not exist!")
413 }
414 }
415 },
416 Save => { self.save_file().unwrap(); },
417 SaveAs => { self.save_file_as().unwrap(); },
418 Print => {
419 let mut printer = printer::Printer::default();
420 if printer.begin_job(0).is_ok() {
421 let (w, h) = printer.printable_rect();
422 self.printable.set_size(w - 40, h - 40);
423 // Needs cleanup
424 let line_count = self.printable.count_lines(0, self.printable.buffer().unwrap().length(), true) / 45;
425 for i in 0..=line_count {
426 self.printable.scroll(45 * i, 0);
427 printer.begin_page().ok();
428 printer.print_widget(&self.printable, 20, 20);
429 printer.end_page().ok();
430 }
431 printer.end_job();
432 }
433 },
434 Quit => {
435 if self.modified {
436 match dialog::choice2(center().0 - 200, center().1 - 100,
437 "Would you like to save your work?", "&Yes", "&No", "") {
438 Some(0) => {
439 if self.save_file().unwrap() {
440 self.app.quit();
441 }
442 },
443 Some(1) => { self.app.quit() },
444 Some(_) | None => (),
445 }
446 } else {
447 self.app.quit();
448 }
449 },
450 Cut => self.editor.cut(),
451 Copy => self.editor.copy(),
452 Paste => self.editor.paste(),
453 About => dialog::message(center().0 - 300, center().1 - 100, "This is an example application written in Rust and using the FLTK Gui library."),
454 }
455 }
456 }
457 }
Sourcepub fn deactivate(&mut self)
pub fn deactivate(&mut self)
Deactivates the menu item
Examples found in repository?
206 pub fn new(args: Vec<String>) -> Self {
207 let app = app::App::default().with_scheme(app::Scheme::Gtk);
208 app::background(211, 211, 211);
209 let (s, r) = app::channel::<Message>();
210 let mut buf = text::TextBuffer::default();
211 buf.set_tab_distance(4);
212 let mut main_win = window::Window::default()
213 .with_size(800, 600)
214 .center_screen()
215 .with_label("RustyEd");
216 let menu = MyMenu::new(&s);
217 let modified = false;
218 menu.menu.find_item("&File/&Save\t").unwrap().deactivate();
219 let mut editor = MyEditor::new(buf.clone());
220 editor.emit(s, Message::Changed);
221 main_win.make_resizable(true);
222 // only resize editor, not the menu bar
223 main_win.resizable(&*editor);
224 main_win.end();
225 main_win.show();
226 main_win.set_callback(move |_| {
227 if app::event() == Event::Close {
228 s.send(Message::Quit);
229 }
230 });
231 let filename = if args.len() > 1 {
232 let file = path::Path::new(&args[1]);
233 assert!(
234 file.exists() && file.is_file(),
235 "An error occurred while opening the file!"
236 );
237 match buf.load_file(&args[1]) {
238 Ok(_) => Some(PathBuf::from(args[1].clone())),
239 Err(e) => {
240 dialog::alert(
241 center().0 - 200,
242 center().1 - 100,
243 &format!("An issue occured while loading the file: {e}"),
244 );
245 None
246 }
247 }
248 } else {
249 None
250 };
251
252 // Handle drag and drop
253 editor.handle({
254 let mut dnd = false;
255 let mut released = false;
256 let buf = buf.clone();
257 move |_, ev| match ev {
258 Event::DndEnter => {
259 dnd = true;
260 true
261 }
262 Event::DndDrag => true,
263 Event::DndRelease => {
264 released = true;
265 true
266 }
267 Event::Paste => {
268 if dnd && released {
269 let path = app::event_text();
270 let path = path.trim();
271 let path = path.replace("file://", "");
272 let path = std::path::PathBuf::from(&path);
273 if path.exists() {
274 // we use a timeout to avoid pasting the path into the buffer
275 app::add_timeout3(0.0, {
276 let mut buf = buf.clone();
277 move |_| match buf.load_file(&path) {
278 Ok(_) => (),
279 Err(e) => dialog::alert(
280 center().0 - 200,
281 center().1 - 100,
282 &format!("An issue occured while loading the file: {e}"),
283 ),
284 }
285 });
286 }
287 dnd = false;
288 released = false;
289 true
290 } else {
291 false
292 }
293 }
294 Event::DndLeave => {
295 dnd = false;
296 released = false;
297 true
298 }
299 _ => false,
300 }
301 });
302
303 // What shows when we attempt to print
304 let mut printable = text::TextDisplay::default();
305 printable.set_frame(FrameType::NoBox);
306 printable.set_scrollbar_size(0);
307 printable.set_buffer(Some(buf.clone()));
308
309 Self {
310 app,
311 modified,
312 filename,
313 r,
314 main_win,
315 menu,
316 buf,
317 editor,
318 printable,
319 }
320 }
321
322 /** Called by "Save", test if file can be written, otherwise call save_file_as()
323 * afterwards. Will return true if the file is succesfully saved. */
324 pub fn save_file(&mut self) -> Result<bool, Box<dyn error::Error>> {
325 match &self.filename {
326 Some(f) => {
327 self.buf.save_file(f)?;
328 self.modified = false;
329 self.menu
330 .menu
331 .find_item("&File/&Save\t")
332 .unwrap()
333 .deactivate();
334 self.menu
335 .menu
336 .find_item("&File/&Quit\t")
337 .unwrap()
338 .set_label_color(Color::Black);
339 let name = match &self.filename {
340 Some(f) => f.to_string_lossy().to_string(),
341 None => "(Untitled)".to_string(),
342 };
343 self.main_win.set_label(&format!("{name} - RustyEd"));
344 Ok(true)
345 }
346 None => self.save_file_as(),
347 }
348 }
349
350 /** Called by "Save As..." or by "Save" in case no file was set yet.
351 * Returns true if the file was succesfully saved. */
352 pub fn save_file_as(&mut self) -> Result<bool, Box<dyn error::Error>> {
353 if let Some(c) = nfc_get_file(dialog::NativeFileChooserType::BrowseSaveFile) {
354 self.buf.save_file(&c)?;
355 self.modified = false;
356 self.menu
357 .menu
358 .find_item("&File/&Save\t")
359 .unwrap()
360 .deactivate();
361 self.menu
362 .menu
363 .find_item("&File/&Quit\t")
364 .unwrap()
365 .set_label_color(Color::Black);
366 self.filename = Some(c);
367 self.main_win
368 .set_label(&format!("{:?} - RustyEd", self.filename.as_ref().unwrap()));
369 Ok(true)
370 } else {
371 Ok(false)
372 }
373 }
Returns whether a menu item is a submenu
Sourcepub fn is_checkbox(&self) -> bool
pub fn is_checkbox(&self) -> bool
Returns whether a menu item is a checkbox
Get the submenu count
Sourcepub unsafe fn user_data(&self) -> Option<Box<dyn FnMut()>>
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
Sourcepub fn set_callback<F: FnMut(&mut Choice) + 'static>(&mut self, cb: F)
pub fn set_callback<F: FnMut(&mut Choice) + 'static>(&mut self, cb: F)
Set a callback for the menu item
Examples found in repository?
20fn main() {
21 let app = fltk::app::App::default();
22
23 // Set panic handler for main thread (will become UI thread)
24 std::panic::set_hook(Box::new({
25 |e| {
26 eprintln!("!!!!PANIC!!!!{:#?}", e);
27 error_box(e.to_string()); // Only works from the UI thread
28 std::process::exit(2);
29 }
30 }));
31
32 let mut main_win = Window::new(
33 2285,
34 180,
35 WIN_WIDTH,
36 WIN_HEIGHT,
37 "FLTK/Terminal Rust wrapper test",
38 );
39 main_win.set_type(WindowType::Double);
40 main_win.make_resizable(true);
41
42 let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44 let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45 term.set_label("term");
46 main_win.resizable(&term);
47 term.set_label_type(LabelType::None);
48
49 let idx = menu_bar.add_choice("Test&1");
50 menu_bar.at(idx).unwrap().set_callback({
51 let mut term1 = term.clone();
52 move |c| mb_test1_cb(c, &mut term1)
53 });
54 menu_bar
55 .at(idx)
56 .unwrap()
57 .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59 let idx = menu_bar.add_choice("Test&2");
60 menu_bar.at(idx).unwrap().set_callback({
61 let mut term1 = term.clone();
62 move |c| mb_test2_cb(c, &mut term1)
63 });
64 menu_bar
65 .at(idx)
66 .unwrap()
67 .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69 let idx = menu_bar.add_choice("Test&3");
70 menu_bar.at(idx).unwrap().set_callback({
71 let mut term1 = term.clone();
72 move |c| mb_test3_cb(c, &mut term1)
73 });
74 menu_bar
75 .at(idx)
76 .unwrap()
77 .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79 let idx = menu_bar.add_choice("Test&4");
80 menu_bar.at(idx).unwrap().set_callback({
81 let mut term1 = term.clone();
82 move |c| mb_test4_cb(c, &mut term1)
83 });
84 menu_bar
85 .at(idx)
86 .unwrap()
87 .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89 let idx = menu_bar.add_choice("Test&5");
90 menu_bar.at(idx).unwrap().set_callback({
91 let mut term1 = term.clone();
92 move |c| mb_test5_cb(c, &mut term1)
93 });
94 menu_bar
95 .at(idx)
96 .unwrap()
97 .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99 menu_bar.end();
100
101 main_win.end();
102 main_win.show();
103
104 // Worker thread that drives the startup tests
105 let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106 let mut term = term.clone();
107 move || {
108 println!("Startup tests\n");
109 term.append("Startup tests\n\n");
110 term.append("<tmp>\n"); // This line will be overwritten later
111
112 term.cursor_up(2, false);
113 assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114 assert_eq!(term.text(true), "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
115
116 // Testing ansi() and set_ansi() methods
117 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
118 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
119 term.set_ansi(false);
120 assert!(!term.ansi());
121 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
122 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
123 term.append_u8(b"Appending u8 array\n");
124 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
125 term.set_ansi(true); // Restore ANSI state
126
127 // Play with the horizontal scrollbar
128 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
129 term.set_hscrollbar_style(ScrollbarStyle::ON);
130 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
131
132 // Test show_unknown() as incidental part of testing append methods
133 term.set_show_unknown(true);
134 assert!(term.show_unknown());
135 term.append_ascii(
136 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
137 );
138 term.set_show_unknown(false);
139 assert!(!term.show_unknown());
140
141 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
142 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
143
144 let r = term.cursor_row();
145 assert_eq!(term.cursor_col(), 0);
146 term.append(&format!("Testing cursor row/col {r}"));
147 assert_eq!(term.cursor_col(), 24);
148 assert_eq!(term.cursor_row(), r);
149
150 // Test cursor color methods
151 assert_eq!(
152 term.cursor_bg_color(),
153 Color::XtermGreen,
154 "Default cursor bg at startup"
155 );
156 assert_eq!(
157 term.cursor_fg_color(),
158 Color::from_hex(0xff_ff_f0),
159 "Default cursor fg at startup"
160 );
161 term.set_cursor_bg_color(Color::Red);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
164 term.set_cursor_fg_color(Color::Blue);
165 assert_eq!(term.cursor_bg_color(), Color::Red);
166 assert_eq!(term.cursor_fg_color(), Color::Blue);
167 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
168 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
169 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
170 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
171
172 // The default display_rows() will derive from the window size
173 let dr = term.display_rows();
174 let height = term.height();
175 assert_eq!(height, term.h());
176 assert!(dr > 20, "Default display_rows at startup");
177 term.resize(term.x(), term.y(), term.w(), height * 2);
178 assert_eq!(term.h(), height * 2);
179 assert_eq!(height * 2, term.h());
180 assert!(term.display_rows() > dr);
181 term.resize(term.x(), term.y(), term.w(), height);
182
183 // The default display_columns() will derive from the window size
184 let dc = term.display_columns();
185 assert!(dc > 80, "Default display_rows at startup");
186 term.set_display_columns(200);
187 assert_eq!(term.display_columns(), 200);
188 term.append("\n 1 2 3 4 5 6 7 8 9");
189 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
190 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
191 term.set_display_columns(90);
192 assert_eq!(term.display_columns(), 90);
193 term.set_display_columns(dc); // Set back to default
194 assert_eq!(term.display_columns(), dc);
195
196 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
197 term.set_history_rows(50);
198 assert_eq!(term.history_rows(), 50);
199 term.set_history_rows(100); // Set back to default
200 assert_eq!(term.history_rows(), 100);
201
202 let hu = term.history_use();
203 term.append(&format!(
204 "history_use = {hu} (it's not clear what this means)\n"
205 ));
206 // assert_eq!(term.history_use(), hu+1);
207
208 term.append(&format!(
209 "margins = b:{} l:{} r:{} t{}\n",
210 term.margin_bottom(),
211 term.margin_left(),
212 term.margin_right(),
213 term.margin_top()
214 ));
215 assert_eq!(term.margin_bottom(), 3);
216 assert_eq!(term.margin_left(), 3);
217 assert_eq!(term.margin_right(), 3);
218 assert_eq!(term.margin_top(), 3);
219
220 term.set_margin_bottom(5);
221 term.set_margin_left(10);
222 term.set_margin_right(15);
223 term.set_margin_top(20);
224 assert_eq!(term.margin_bottom(), 5);
225 assert_eq!(term.margin_left(), 10);
226 assert_eq!(term.margin_right(), 15);
227 assert_eq!(term.margin_top(), 20);
228
229 term.append("Single character: '");
230 term.print_char('X');
231 term.append("', single UTF-8 character: '");
232 term.print_char_utf8('↑');
233 term.append("'\n");
234
235 let rr = term.redraw_rate();
236 assert_eq!(rr, 0.1, "Default redraw rate at startup");
237 term.append(&format!("Redraw rate {rr}\n"));
238 term.set_redraw_rate(1.0);
239 assert_eq!(term.redraw_rate(), 1.0);
240 term.set_redraw_rate(rr);
241 assert_eq!(term.redraw_rate(), rr);
242
243 let rs = term.redraw_style();
244 term.append(&format!("Redraw style {rs:?}\n"));
245 assert_eq!(
246 rs,
247 RedrawStyle::RateLimited,
248 "Default redraw style at startup"
249 );
250 term.set_redraw_style(RedrawStyle::NoRedraw);
251 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
252 term.set_redraw_style(rs);
253 assert_eq!(term.redraw_style(), rs);
254
255 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
256 assert_eq!(
257 RedrawStyle::NoRedraw.bits(),
258 0x0000,
259 "RedrawStyle enum values have been reassigned"
260 );
261 assert_eq!(
262 RedrawStyle::RateLimited.bits(),
263 0x0001,
264 "RedrawStyle enum values have been reassigned"
265 );
266 assert_eq!(
267 RedrawStyle::PerWrite.bits(),
268 0x0002,
269 "RedrawStyle enum values have been reassigned"
270 );
271
272 let sb = term.scrollbar();
273 let hsb = term.hscrollbar();
274 // Both vertical and horizontal scrollbars are at zero
275 assert_eq!(sb.value(), 0.0);
276 assert_eq!(hsb.value(), 0.0);
277 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
278
279 term.append(&format!(
280 "Scrollbar actual size {}\n",
281 term.scrollbar_actual_size()
282 ));
283 assert_eq!(term.scrollbar_actual_size(), 16);
284 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
285 assert_eq!(
286 term.scrollbar_size(),
287 0,
288 "Default scrollbar size at startup"
289 );
290 term.set_scrollbar_size(40);
291 assert_eq!(term.scrollbar_size(), 40);
292 assert_eq!(term.scrollbar_actual_size(), 40);
293 term.append(&format!(
294 "Scrollbar actual size {}\n",
295 term.scrollbar_actual_size()
296 ));
297 term.set_scrollbar_size(0); // Restore default
298 assert_eq!(term.scrollbar_size(), 0);
299 assert_eq!(term.scrollbar_actual_size(), 16);
300
301 let sfc = term.selection_fg_color();
302 let sbc = term.selection_bg_color();
303 assert_eq!(sfc, Color::Black);
304 assert_eq!(sbc, Color::White);
305 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
306 term.set_selection_fg_color(Color::Green);
307 term.set_selection_bg_color(Color::DarkBlue);
308 assert_eq!(term.selection_fg_color(), Color::Green);
309 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
310 term.set_selection_fg_color(sfc);
311 term.set_selection_bg_color(sbc);
312 assert_eq!(term.selection_fg_color(), Color::Black);
313 assert_eq!(term.selection_bg_color(), Color::White);
314
315 let tfcd = term.text_fg_color_default();
316 let tbcd = term.text_bg_color_default();
317 assert_eq!(tfcd, Color::XtermWhite);
318 assert_eq!(tbcd, Color::TransparentBg);
319 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
320 term.set_text_fg_color_default(Color::Green);
321 term.set_text_bg_color_default(Color::DarkBlue);
322 assert_eq!(term.text_fg_color_default(), Color::Green);
323 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
324 term.set_text_fg_color_default(tfcd);
325 term.set_text_bg_color_default(tbcd);
326 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
327 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
328
329 let tfc = term.text_fg_color();
330 let tbc = term.text_bg_color();
331 assert_eq!(tfc, Color::XtermWhite);
332 assert_eq!(tbc, Color::TransparentBg);
333 term.append(&format!("Text colors: {sfc} {sbc}\n"));
334 term.set_text_fg_color(Color::Green);
335 term.set_text_bg_color(Color::DarkBlue);
336 assert_eq!(term.text_fg_color(), Color::Green);
337 assert_eq!(term.text_bg_color(), Color::DarkBlue);
338 term.set_text_fg_color(tfc);
339 term.set_text_bg_color(tbc);
340 assert_eq!(term.text_fg_color(), Color::XtermWhite);
341 assert_eq!(term.text_bg_color(), Color::TransparentBg);
342
343 let tf = term.text_font();
344 term.append(&format!("Text font: {tf:?}\n"));
345 assert_eq!(tf, Font::Courier);
346 term.set_text_font(Font::Screen);
347 assert_eq!(term.text_font(), Font::Screen);
348 term.set_text_font(tf);
349 assert_eq!(term.text_font(), Font::Courier);
350
351 let ts = term.text_size();
352 let r = term.h_to_row(100);
353 let c = term.w_to_col(100);
354 term.append(&format!(
355 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
356 ));
357 assert_eq!(ts, 14);
358 term.set_text_size(30);
359 assert_eq!(term.text_size(), 30);
360 term.append(&format!(
361 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
362 term.text_size(),
363 term.h_to_row(100),
364 term.w_to_col(100)
365 ));
366 term.set_text_size(ts);
367 assert_eq!(term.text_size(), ts);
368 term.append(&format!(
369 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
370 term.text_size(),
371 term.h_to_row(100),
372 term.w_to_col(100)
373 ));
374
375 // Keyboard handler
376 term.handle({
377 move |term, e| {
378 match e {
379 fltk::enums::Event::KeyDown
380 if fltk::app::event_key() == fltk::enums::Key::Escape =>
381 {
382 // false to let FLTK handle ESC. true to hide ESC
383 false
384 }
385
386 fltk::enums::Event::KeyDown
387 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
388 {
389 // We handle control keystroke
390 let k = fltk::app::event_text();
391 term.append_utf8(&k);
392 true
393 }
394
395 fltk::enums::Event::KeyDown
396 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
397 {
398 // We handle normal printable keystroke
399 let k = fltk::app::event_text();
400 term.take_focus().unwrap();
401 term.append(&k);
402 true
403 }
404
405 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
406 // We can do this, or else ignore them (return false)
407 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
408 // term.redraw();
409 // true
410 // }
411 _ => false, // Let FLTK handle everything else
412 }
413 }
414 });
415
416 let attr_save = term.text_attrib();
417 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
418 term.append("\nStartup tests complete. Keyboard is live.\n");
419 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
420 term.set_text_attrib(attr_save);
421 assert_eq!(term.text_attrib(), attr_save);
422 term.redraw();
423 }
424 });
425
426 app.run().unwrap();
427}
Sourcepub fn do_callback<W: MenuExt>(&mut self, w: &W)
pub fn do_callback<W: MenuExt>(&mut self, w: &W)
Run the menu item’s callback
Sourcepub fn emit<T: 'static + Clone + Send + Sync>(
&mut self,
sender: Sender<T>,
msg: T,
)
pub fn emit<T: 'static + Clone + Send + Sync>( &mut self, sender: Sender<T>, msg: T, )
Use a sender to send a message during callback
Sourcepub fn was_deleted(&self) -> bool
pub fn was_deleted(&self) -> bool
Check if a menu item was deleted
Sourcepub fn draw<M: MenuExt>(
&self,
x: i32,
y: i32,
w: i32,
h: i32,
menu: &M,
selected: bool,
)
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
Sourcepub fn add_image<I: ImageExt>(&mut self, image: Option<I>, on_left: bool)
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);
}
Sourcepub fn add<F: FnMut(&mut Choice) + 'static>(
&mut self,
name: &str,
shortcut: Shortcut,
flag: MenuFlag,
cb: F,
) -> i32
pub fn add<F: FnMut(&mut Choice) + 'static>( &mut self, name: &str, shortcut: Shortcut, flag: MenuFlag, cb: F, ) -> i32
Add a menu item
Sourcepub fn insert<F: FnMut(&mut Choice) + 'static>(
&mut self,
idx: i32,
name: &str,
shortcut: Shortcut,
flag: MenuFlag,
cb: F,
) -> i32
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
Sourcepub fn add_emit<T: 'static + Clone + Send + Sync>(
&mut self,
label: &str,
shortcut: Shortcut,
flag: MenuFlag,
sender: Sender<T>,
msg: T,
) -> i32
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).
Sourcepub fn insert_emit<T: 'static + Clone + Send + Sync>(
&mut self,
idx: i32,
label: &str,
shortcut: Shortcut,
flag: MenuFlag,
sender: Sender<T>,
msg: T,
) -> i32
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).
Sourcepub fn set_shortcut(&mut self, shortcut: Shortcut)
pub fn set_shortcut(&mut self, shortcut: Shortcut)
Set the menu item’s shortcut
Examples found in repository?
20fn main() {
21 let app = fltk::app::App::default();
22
23 // Set panic handler for main thread (will become UI thread)
24 std::panic::set_hook(Box::new({
25 |e| {
26 eprintln!("!!!!PANIC!!!!{:#?}", e);
27 error_box(e.to_string()); // Only works from the UI thread
28 std::process::exit(2);
29 }
30 }));
31
32 let mut main_win = Window::new(
33 2285,
34 180,
35 WIN_WIDTH,
36 WIN_HEIGHT,
37 "FLTK/Terminal Rust wrapper test",
38 );
39 main_win.set_type(WindowType::Double);
40 main_win.make_resizable(true);
41
42 let mut menu_bar = MenuBar::new(0, 0, WIN_WIDTH, 30, None);
43
44 let mut term = Terminal::new(0, 30, WIN_WIDTH, WIN_HEIGHT - 30, None);
45 term.set_label("term");
46 main_win.resizable(&term);
47 term.set_label_type(LabelType::None);
48
49 let idx = menu_bar.add_choice("Test&1");
50 menu_bar.at(idx).unwrap().set_callback({
51 let mut term1 = term.clone();
52 move |c| mb_test1_cb(c, &mut term1)
53 });
54 menu_bar
55 .at(idx)
56 .unwrap()
57 .set_shortcut(unsafe { std::mem::transmute(0x80031) }); // Alt-1
58
59 let idx = menu_bar.add_choice("Test&2");
60 menu_bar.at(idx).unwrap().set_callback({
61 let mut term1 = term.clone();
62 move |c| mb_test2_cb(c, &mut term1)
63 });
64 menu_bar
65 .at(idx)
66 .unwrap()
67 .set_shortcut(unsafe { std::mem::transmute(0x80032) }); // Alt-2
68
69 let idx = menu_bar.add_choice("Test&3");
70 menu_bar.at(idx).unwrap().set_callback({
71 let mut term1 = term.clone();
72 move |c| mb_test3_cb(c, &mut term1)
73 });
74 menu_bar
75 .at(idx)
76 .unwrap()
77 .set_shortcut(unsafe { std::mem::transmute(0x80033) }); // Alt-3
78
79 let idx = menu_bar.add_choice("Test&4");
80 menu_bar.at(idx).unwrap().set_callback({
81 let mut term1 = term.clone();
82 move |c| mb_test4_cb(c, &mut term1)
83 });
84 menu_bar
85 .at(idx)
86 .unwrap()
87 .set_shortcut(unsafe { std::mem::transmute(0x80034) }); // Alt-4
88
89 let idx = menu_bar.add_choice("Test&5");
90 menu_bar.at(idx).unwrap().set_callback({
91 let mut term1 = term.clone();
92 move |c| mb_test5_cb(c, &mut term1)
93 });
94 menu_bar
95 .at(idx)
96 .unwrap()
97 .set_shortcut(unsafe { std::mem::transmute(0x80035) }); // Alt-5
98
99 menu_bar.end();
100
101 main_win.end();
102 main_win.show();
103
104 // Worker thread that drives the startup tests
105 let _worker_thread: std::thread::JoinHandle<_> = std::thread::spawn({
106 let mut term = term.clone();
107 move || {
108 println!("Startup tests\n");
109 term.append("Startup tests\n\n");
110 term.append("<tmp>\n"); // This line will be overwritten later
111
112 term.cursor_up(2, false);
113 assert_eq!(term.text(false), "Startup tests\n\n"); // Ignores lines below cursor
114 assert_eq!(term.text(true), "Startup tests\n\n<tmp>\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
115
116 // Testing ansi() and set_ansi() methods
117 assert!(term.ansi(), "Default ANSI mode should be ON at startup");
118 term.append("ANSI mode is \x1b[4mON\x1b[0m\n");
119 term.set_ansi(false);
120 assert!(!term.ansi());
121 term.append("ANSI mode is \x1b[4mOFF\x1b[0m\n");
122 // append() method is already being used/tested. Test the u8, ascii, and utf8 variants
123 term.append_u8(b"Appending u8 array\n");
124 term.append_ascii("Appending ASCII array ↑ (up-arrow is dropped)\n");
125 term.set_ansi(true); // Restore ANSI state
126
127 // Play with the horizontal scrollbar
128 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::AUTO);
129 term.set_hscrollbar_style(ScrollbarStyle::ON);
130 assert_eq!(term.hscrollbar_style(), ScrollbarStyle::ON);
131
132 // Test show_unknown() as incidental part of testing append methods
133 term.set_show_unknown(true);
134 assert!(term.show_unknown());
135 term.append_ascii(
136 "Appending ASCII array with show_unknown() ↑ (up-arrow is three unknown bytes)\n",
137 );
138 term.set_show_unknown(false);
139 assert!(!term.show_unknown());
140
141 term.append_utf8("Appending UTF8 array ↑ (up-arrow is visible)\n");
142 term.append_utf8_u8(b"Appending UTF8 array as u8 \xe2\x86\x91 (up-arrow is visible)\n");
143
144 let r = term.cursor_row();
145 assert_eq!(term.cursor_col(), 0);
146 term.append(&format!("Testing cursor row/col {r}"));
147 assert_eq!(term.cursor_col(), 24);
148 assert_eq!(term.cursor_row(), r);
149
150 // Test cursor color methods
151 assert_eq!(
152 term.cursor_bg_color(),
153 Color::XtermGreen,
154 "Default cursor bg at startup"
155 );
156 assert_eq!(
157 term.cursor_fg_color(),
158 Color::from_hex(0xff_ff_f0),
159 "Default cursor fg at startup"
160 );
161 term.set_cursor_bg_color(Color::Red);
162 assert_eq!(term.cursor_bg_color(), Color::Red);
163 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
164 term.set_cursor_fg_color(Color::Blue);
165 assert_eq!(term.cursor_bg_color(), Color::Red);
166 assert_eq!(term.cursor_fg_color(), Color::Blue);
167 term.set_cursor_bg_color(Color::XtermGreen); // Restore the defaults
168 term.set_cursor_fg_color(Color::from_hex(0xff_ff_f0));
169 assert_eq!(term.cursor_bg_color(), Color::XtermGreen);
170 assert_eq!(term.cursor_fg_color(), Color::from_hex(0xff_ff_f0));
171
172 // The default display_rows() will derive from the window size
173 let dr = term.display_rows();
174 let height = term.height();
175 assert_eq!(height, term.h());
176 assert!(dr > 20, "Default display_rows at startup");
177 term.resize(term.x(), term.y(), term.w(), height * 2);
178 assert_eq!(term.h(), height * 2);
179 assert_eq!(height * 2, term.h());
180 assert!(term.display_rows() > dr);
181 term.resize(term.x(), term.y(), term.w(), height);
182
183 // The default display_columns() will derive from the window size
184 let dc = term.display_columns();
185 assert!(dc > 80, "Default display_rows at startup");
186 term.set_display_columns(200);
187 assert_eq!(term.display_columns(), 200);
188 term.append("\n 1 2 3 4 5 6 7 8 9");
189 term.append("\n123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
190 term.append("[This text should be truncated by display_columns() call below.]\n"); // We shouldn't see this on screen
191 term.set_display_columns(90);
192 assert_eq!(term.display_columns(), 90);
193 term.set_display_columns(dc); // Set back to default
194 assert_eq!(term.display_columns(), dc);
195
196 assert_eq!(term.history_rows(), 100, "Default history_rows at startup");
197 term.set_history_rows(50);
198 assert_eq!(term.history_rows(), 50);
199 term.set_history_rows(100); // Set back to default
200 assert_eq!(term.history_rows(), 100);
201
202 let hu = term.history_use();
203 term.append(&format!(
204 "history_use = {hu} (it's not clear what this means)\n"
205 ));
206 // assert_eq!(term.history_use(), hu+1);
207
208 term.append(&format!(
209 "margins = b:{} l:{} r:{} t{}\n",
210 term.margin_bottom(),
211 term.margin_left(),
212 term.margin_right(),
213 term.margin_top()
214 ));
215 assert_eq!(term.margin_bottom(), 3);
216 assert_eq!(term.margin_left(), 3);
217 assert_eq!(term.margin_right(), 3);
218 assert_eq!(term.margin_top(), 3);
219
220 term.set_margin_bottom(5);
221 term.set_margin_left(10);
222 term.set_margin_right(15);
223 term.set_margin_top(20);
224 assert_eq!(term.margin_bottom(), 5);
225 assert_eq!(term.margin_left(), 10);
226 assert_eq!(term.margin_right(), 15);
227 assert_eq!(term.margin_top(), 20);
228
229 term.append("Single character: '");
230 term.print_char('X');
231 term.append("', single UTF-8 character: '");
232 term.print_char_utf8('↑');
233 term.append("'\n");
234
235 let rr = term.redraw_rate();
236 assert_eq!(rr, 0.1, "Default redraw rate at startup");
237 term.append(&format!("Redraw rate {rr}\n"));
238 term.set_redraw_rate(1.0);
239 assert_eq!(term.redraw_rate(), 1.0);
240 term.set_redraw_rate(rr);
241 assert_eq!(term.redraw_rate(), rr);
242
243 let rs = term.redraw_style();
244 term.append(&format!("Redraw style {rs:?}\n"));
245 assert_eq!(
246 rs,
247 RedrawStyle::RateLimited,
248 "Default redraw style at startup"
249 );
250 term.set_redraw_style(RedrawStyle::NoRedraw);
251 assert_eq!(term.redraw_style(), RedrawStyle::NoRedraw);
252 term.set_redraw_style(rs);
253 assert_eq!(term.redraw_style(), rs);
254
255 // Sanity checks: enum values are implicitly assigned in the C++ code so could change unexpectedly
256 assert_eq!(
257 RedrawStyle::NoRedraw.bits(),
258 0x0000,
259 "RedrawStyle enum values have been reassigned"
260 );
261 assert_eq!(
262 RedrawStyle::RateLimited.bits(),
263 0x0001,
264 "RedrawStyle enum values have been reassigned"
265 );
266 assert_eq!(
267 RedrawStyle::PerWrite.bits(),
268 0x0002,
269 "RedrawStyle enum values have been reassigned"
270 );
271
272 let sb = term.scrollbar();
273 let hsb = term.hscrollbar();
274 // Both vertical and horizontal scrollbars are at zero
275 assert_eq!(sb.value(), 0.0);
276 assert_eq!(hsb.value(), 0.0);
277 term.set_hscrollbar_style(ScrollbarStyle::AUTO);
278
279 term.append(&format!(
280 "Scrollbar actual size {}\n",
281 term.scrollbar_actual_size()
282 ));
283 assert_eq!(term.scrollbar_actual_size(), 16);
284 term.append(&format!("Scrollbar size {}\n", term.scrollbar_size()));
285 assert_eq!(
286 term.scrollbar_size(),
287 0,
288 "Default scrollbar size at startup"
289 );
290 term.set_scrollbar_size(40);
291 assert_eq!(term.scrollbar_size(), 40);
292 assert_eq!(term.scrollbar_actual_size(), 40);
293 term.append(&format!(
294 "Scrollbar actual size {}\n",
295 term.scrollbar_actual_size()
296 ));
297 term.set_scrollbar_size(0); // Restore default
298 assert_eq!(term.scrollbar_size(), 0);
299 assert_eq!(term.scrollbar_actual_size(), 16);
300
301 let sfc = term.selection_fg_color();
302 let sbc = term.selection_bg_color();
303 assert_eq!(sfc, Color::Black);
304 assert_eq!(sbc, Color::White);
305 term.append(&format!("Selection colors: {sfc} {sbc}\n"));
306 term.set_selection_fg_color(Color::Green);
307 term.set_selection_bg_color(Color::DarkBlue);
308 assert_eq!(term.selection_fg_color(), Color::Green);
309 assert_eq!(term.selection_bg_color(), Color::DarkBlue);
310 term.set_selection_fg_color(sfc);
311 term.set_selection_bg_color(sbc);
312 assert_eq!(term.selection_fg_color(), Color::Black);
313 assert_eq!(term.selection_bg_color(), Color::White);
314
315 let tfcd = term.text_fg_color_default();
316 let tbcd = term.text_bg_color_default();
317 assert_eq!(tfcd, Color::XtermWhite);
318 assert_eq!(tbcd, Color::TransparentBg);
319 term.append(&format!("Default text colors: {sfc} {sbc}\n"));
320 term.set_text_fg_color_default(Color::Green);
321 term.set_text_bg_color_default(Color::DarkBlue);
322 assert_eq!(term.text_fg_color_default(), Color::Green);
323 assert_eq!(term.text_bg_color_default(), Color::DarkBlue);
324 term.set_text_fg_color_default(tfcd);
325 term.set_text_bg_color_default(tbcd);
326 assert_eq!(term.text_fg_color_default(), Color::XtermWhite);
327 assert_eq!(term.text_bg_color_default(), Color::TransparentBg);
328
329 let tfc = term.text_fg_color();
330 let tbc = term.text_bg_color();
331 assert_eq!(tfc, Color::XtermWhite);
332 assert_eq!(tbc, Color::TransparentBg);
333 term.append(&format!("Text colors: {sfc} {sbc}\n"));
334 term.set_text_fg_color(Color::Green);
335 term.set_text_bg_color(Color::DarkBlue);
336 assert_eq!(term.text_fg_color(), Color::Green);
337 assert_eq!(term.text_bg_color(), Color::DarkBlue);
338 term.set_text_fg_color(tfc);
339 term.set_text_bg_color(tbc);
340 assert_eq!(term.text_fg_color(), Color::XtermWhite);
341 assert_eq!(term.text_bg_color(), Color::TransparentBg);
342
343 let tf = term.text_font();
344 term.append(&format!("Text font: {tf:?}\n"));
345 assert_eq!(tf, Font::Courier);
346 term.set_text_font(Font::Screen);
347 assert_eq!(term.text_font(), Font::Screen);
348 term.set_text_font(tf);
349 assert_eq!(term.text_font(), Font::Courier);
350
351 let ts = term.text_size();
352 let r = term.h_to_row(100);
353 let c = term.w_to_col(100);
354 term.append(&format!(
355 "Text size: {ts}, h_to_row(100): {r}, w_to_col(100): {c}\n"
356 ));
357 assert_eq!(ts, 14);
358 term.set_text_size(30);
359 assert_eq!(term.text_size(), 30);
360 term.append(&format!(
361 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
362 term.text_size(),
363 term.h_to_row(100),
364 term.w_to_col(100)
365 ));
366 term.set_text_size(ts);
367 assert_eq!(term.text_size(), ts);
368 term.append(&format!(
369 "Text size: {}, h_to_row(100): {}, w_to_col(100): {}\n",
370 term.text_size(),
371 term.h_to_row(100),
372 term.w_to_col(100)
373 ));
374
375 // Keyboard handler
376 term.handle({
377 move |term, e| {
378 match e {
379 fltk::enums::Event::KeyDown
380 if fltk::app::event_key() == fltk::enums::Key::Escape =>
381 {
382 // false to let FLTK handle ESC. true to hide ESC
383 false
384 }
385
386 fltk::enums::Event::KeyDown
387 if fltk::app::event_length() == 1 && fltk::app::is_event_ctrl() =>
388 {
389 // We handle control keystroke
390 let k = fltk::app::event_text();
391 term.append_utf8(&k);
392 true
393 }
394
395 fltk::enums::Event::KeyDown
396 if fltk::app::event_length() == 1 && !fltk::app::is_event_alt() =>
397 {
398 // We handle normal printable keystroke
399 let k = fltk::app::event_text();
400 term.take_focus().unwrap();
401 term.append(&k);
402 true
403 }
404
405 // fltk docs say that keyboard handler should always claim Focus and Unfocus events
406 // We can do this, or else ignore them (return false)
407 // fltk::enums::Event::Focus | fltk::enums::Event::Unfocus => {
408 // term.redraw();
409 // true
410 // }
411 _ => false, // Let FLTK handle everything else
412 }
413 }
414 });
415
416 let attr_save = term.text_attrib();
417 term.set_text_attrib(Attrib::Inverse | Attrib::Italic);
418 term.append("\nStartup tests complete. Keyboard is live.\n");
419 assert_eq!(term.text_attrib(), Attrib::Inverse | Attrib::Italic);
420 term.set_text_attrib(attr_save);
421 assert_eq!(term.text_attrib(), attr_save);
422 term.redraw();
423 }
424 });
425
426 app.run().unwrap();
427}
Trait Implementations§
Source§impl IntoIterator for MenuItem
impl IntoIterator for MenuItem
impl Eq for MenuItem
impl Send for MenuItem
single-threaded
only.impl Sync for MenuItem
single-threaded
only.