diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/app.rs | 308 | ||||
-rw-r--r-- | src/command.rs | 134 | ||||
-rw-r--r-- | src/main.rs | 36 | ||||
-rw-r--r-- | src/utils.rs | 17 |
4 files changed, 396 insertions, 99 deletions
@@ -23,21 +23,29 @@ use sdl2::{ | |||
23 | Sdl, | 23 | Sdl, |
24 | }; | 24 | }; |
25 | 25 | ||
26 | #[derive(Debug, Copy, Clone, PartialEq)] | ||
27 | pub enum Mode { | ||
28 | Draw, | ||
29 | Command, | ||
30 | } | ||
31 | |||
26 | pub struct AppState<'ctx> { | 32 | pub struct AppState<'ctx> { |
27 | active_color: bool, | 33 | active_color: bool, |
28 | brush_size: u8, | 34 | brush_size: u8, |
29 | dither_level: u8, | ||
30 | canvas: Canvas<Window>, | 35 | canvas: Canvas<Window>, |
31 | context: &'ctx Sdl, | 36 | context: &'ctx Sdl, |
32 | ttf_context: &'ctx Sdl2TtfContext, | ||
33 | mouse: (i32, i32), | ||
34 | current_operation: Operation, | 37 | current_operation: Operation, |
38 | dither_level: u8, | ||
35 | grid: Grid, | 39 | grid: Grid, |
36 | last_point: Option<Point>, | 40 | last_point: Option<MapPoint>, |
41 | mode: Mode, | ||
42 | mouse: (i32, i32), | ||
37 | pixmap: Pixmap<bool>, | 43 | pixmap: Pixmap<bool>, |
38 | start: Point, | 44 | start: Point, |
39 | symmetry: Symmetry, | 45 | symmetry: Symmetry, |
46 | ttf_context: &'ctx Sdl2TtfContext, | ||
40 | undo_stack: UndoStack<Operation>, | 47 | undo_stack: UndoStack<Operation>, |
48 | command_box: CommandBox, | ||
41 | zoom: u8, | 49 | zoom: u8, |
42 | } | 50 | } |
43 | 51 | ||
@@ -153,15 +161,15 @@ impl<'ctx> AppState<'ctx> { | |||
153 | 161 | ||
154 | fn paint_line<P: Into<Point>>( | 162 | fn paint_line<P: Into<Point>>( |
155 | &mut self, | 163 | &mut self, |
156 | start: P, | 164 | start: MapPoint, |
157 | end: P, | 165 | end: P, |
158 | val: bool, | 166 | val: bool, |
159 | ) -> Result<Vec<ModifyRecord>, ()> { | 167 | ) -> Result<Vec<ModifyRecord>, ()> { |
160 | let start = self.idx_at_coord(start).ok_or(())?; | 168 | let MapPoint { x, y } = start; |
161 | let end = self.idx_at_coord(end).ok_or(())?; | 169 | let end = self.idx_at_coord(end).ok_or(())?; |
162 | let dither_level = self.dither_level; | 170 | let dither_level = self.dither_level; |
163 | 171 | ||
164 | let line = self.pixmap.get_line(start, end); | 172 | let line = self.pixmap.get_line((x, y), end); |
165 | let sym_line = self.symmetry.apply(&line); | 173 | let sym_line = self.symmetry.apply(&line); |
166 | 174 | ||
167 | let mut line_modify_record = vec![]; | 175 | let mut line_modify_record = vec![]; |
@@ -239,6 +247,21 @@ impl<'ctx> AppState<'ctx> { | |||
239 | } | 247 | } |
240 | } | 248 | } |
241 | 249 | ||
250 | fn eval_command(&mut self) { | ||
251 | match self.command_box.text.as_str() { | ||
252 | "(save)" => { | ||
253 | let image = self.export(); | ||
254 | let encoded = image.encode().unwrap(); | ||
255 | let mut buffer = File::create("test.obi").unwrap(); | ||
256 | eprintln!("writing to file"); | ||
257 | buffer.write_all(&encoded[..]).unwrap(); | ||
258 | } | ||
259 | _ => {} | ||
260 | } | ||
261 | self.command_box.clear(); | ||
262 | self.mode = Mode::Draw; | ||
263 | } | ||
264 | |||
242 | fn zoom_out(&mut self, p: (i32, i32)) { | 265 | fn zoom_out(&mut self, p: (i32, i32)) { |
243 | if self.zoom > 1 { | 266 | if self.zoom > 1 { |
244 | // attempt to center around cursor | 267 | // attempt to center around cursor |
@@ -309,6 +332,27 @@ impl<'ctx> AppState<'ctx> { | |||
309 | ); | 332 | ); |
310 | } | 333 | } |
311 | 334 | ||
335 | fn draw_command_box(&mut self) { | ||
336 | if self.command_box.is_empty() { | ||
337 | self.mode = Mode::Draw; | ||
338 | return; | ||
339 | } | ||
340 | let (winsize_x, winsize_y) = self.canvas.window().size(); | ||
341 | let cmd_height: u32 = 20; | ||
342 | let cmd_width = winsize_x; | ||
343 | self.canvas.set_draw_color(WHITE); | ||
344 | self.canvas | ||
345 | .fill_rect(rect!(0, winsize_y - cmd_height, cmd_width, cmd_height)) | ||
346 | .unwrap(); | ||
347 | draw_text( | ||
348 | &mut self.canvas, | ||
349 | self.ttf_context, | ||
350 | &self.command_box.text[..], | ||
351 | BLACK, | ||
352 | (0, winsize_y - cmd_height), | ||
353 | ); | ||
354 | } | ||
355 | |||
312 | fn draw_mouse(&mut self) { | 356 | fn draw_mouse(&mut self) { |
313 | let brush_size = self.brush_size; | 357 | let brush_size = self.brush_size; |
314 | let cs = self.zoom as u32; | 358 | let cs = self.zoom as u32; |
@@ -368,7 +412,11 @@ impl<'ctx> AppState<'ctx> { | |||
368 | .draw_line((line_coord, 0), (line_coord, winsize_y as i32)) | 412 | .draw_line((line_coord, 0), (line_coord, winsize_y as i32)) |
369 | .unwrap(); | 413 | .unwrap(); |
370 | } | 414 | } |
371 | self.draw_statusline(); | 415 | if self.mode == Mode::Draw { |
416 | self.draw_statusline(); | ||
417 | } else { | ||
418 | self.draw_command_box(); | ||
419 | } | ||
372 | self.draw_mouse(); | 420 | self.draw_mouse(); |
373 | } | 421 | } |
374 | 422 | ||
@@ -446,109 +494,175 @@ impl<'ctx> AppState<'ctx> { | |||
446 | let mouse = event_pump.mouse_state(); | 494 | let mouse = event_pump.mouse_state(); |
447 | self.mouse = (mouse.x(), mouse.y()); | 495 | self.mouse = (mouse.x(), mouse.y()); |
448 | for event in event_pump.poll_iter() { | 496 | for event in event_pump.poll_iter() { |
449 | match event { | 497 | if let Event::KeyDown { |
450 | Event::KeyDown { | 498 | keycode: Some(Keycode::Num9), |
451 | keycode: Some(k), .. | 499 | keymod, |
452 | } => { | 500 | .. |
453 | match k { | 501 | } = event |
454 | // pan | 502 | { |
455 | Keycode::W => self.pan((0, 10)), | 503 | if keymod == Mod::LSHIFTMOD || keymod == Mod::RSHIFTMOD { |
456 | Keycode::A => self.pan((10, 0)), | 504 | self.mode = Mode::Command; |
457 | Keycode::S => self.pan((0, -10)), | 505 | } |
458 | Keycode::D => self.pan((-10, 0)), | 506 | } |
459 | // zoom | 507 | match self.mode { |
460 | Keycode::C => { | 508 | Mode::Draw => { |
461 | let cursor = (mouse.x(), mouse.y()); | 509 | match event { |
462 | self.zoom_in(cursor); | 510 | Event::KeyDown { |
463 | } | 511 | keycode: Some(k), .. |
464 | Keycode::Z => { | 512 | } => { |
465 | let cursor = (mouse.x(), mouse.y()); | 513 | match k { |
466 | self.zoom_out(cursor); | 514 | // pan |
467 | } | 515 | Keycode::W => self.pan((0, 10)), |
468 | // brush ops | 516 | Keycode::A => self.pan((10, 0)), |
469 | Keycode::Q => self.decrease_brush_size(), | 517 | Keycode::S => self.pan((0, -10)), |
470 | Keycode::E => self.increase_brush_size(), | 518 | Keycode::D => self.pan((-10, 0)), |
471 | Keycode::Num1 => self.reduce_intensity(), | 519 | // zoom |
472 | Keycode::Num3 => self.increase_intensity(), | 520 | Keycode::C => { |
473 | // flip color | 521 | let cursor = (mouse.x(), mouse.y()); |
474 | Keycode::X => self.change_active_color(), | 522 | self.zoom_in(cursor); |
475 | // toggle grid | ||
476 | Keycode::Tab => self.toggle_grid(), | ||
477 | // line drawing | ||
478 | Keycode::F => { | ||
479 | let end = (mouse.x(), mouse.y()).into(); | ||
480 | if let Some(start) = self.last_point { | ||
481 | if let Ok(o) = self.paint_line(start, end, self.active_color) { | ||
482 | self.commit_operation(); | ||
483 | self.current_operation = | ||
484 | o.into_iter().filter(|v| !v.old_val == v.val).collect(); | ||
485 | self.commit_operation(); | ||
486 | self.last_point = Some(end); | ||
487 | } | 523 | } |
524 | Keycode::Z => { | ||
525 | let cursor = (mouse.x(), mouse.y()); | ||
526 | self.zoom_out(cursor); | ||
527 | } | ||
528 | // brush ops | ||
529 | Keycode::Q => self.decrease_brush_size(), | ||
530 | Keycode::E => self.increase_brush_size(), | ||
531 | Keycode::Num1 => self.reduce_intensity(), | ||
532 | Keycode::Num3 => self.increase_intensity(), | ||
533 | // flip color | ||
534 | Keycode::X => self.change_active_color(), | ||
535 | // toggle grid | ||
536 | Keycode::Tab => self.toggle_grid(), | ||
537 | // line drawing | ||
538 | Keycode::F => { | ||
539 | let end: Point = (mouse.x(), mouse.y()).into(); | ||
540 | if let Some(start) = self.last_point { | ||
541 | if let Ok(o) = | ||
542 | self.paint_line(start, end, self.active_color) | ||
543 | { | ||
544 | self.commit_operation(); | ||
545 | self.current_operation = o | ||
546 | .into_iter() | ||
547 | .filter(|v| !v.old_val == v.val) | ||
548 | .collect(); | ||
549 | self.commit_operation(); | ||
550 | self.last_point = | ||
551 | Some(self.idx_at_coord(end).unwrap().into()); | ||
552 | } | ||
553 | } | ||
554 | } | ||
555 | Keycode::V => self.cycle_symmetry(), | ||
556 | // exit | ||
557 | Keycode::Escape => break 'running, | ||
558 | // undo & redo | ||
559 | Keycode::U => { | ||
560 | if let Some(op) = self.undo_stack.undo() { | ||
561 | self.apply_operation(op, OpKind::Undo); | ||
562 | } | ||
563 | } | ||
564 | // export to file | ||
565 | Keycode::N => { | ||
566 | let image = self.export(); | ||
567 | let encoded = image.encode().unwrap(); | ||
568 | let mut buffer = File::create("test.obi").unwrap(); | ||
569 | eprintln!("writing to file"); | ||
570 | buffer.write_all(&encoded[..]).unwrap(); | ||
571 | } | ||
572 | Keycode::R => { | ||
573 | if let Some(op) = self.undo_stack.redo() { | ||
574 | self.apply_operation(op, OpKind::Redo); | ||
575 | } | ||
576 | } | ||
577 | _ => (), | ||
488 | } | 578 | } |
489 | } | 579 | } |
490 | Keycode::V => self.cycle_symmetry(), | 580 | // start of operation |
491 | // exit | 581 | Event::MouseButtonDown { |
492 | Keycode::Escape => break 'running, | 582 | x, y, mouse_btn, .. |
493 | // undo & redo | 583 | } => { |
494 | Keycode::U => { | 584 | let pt = (x, y); |
495 | if let Some(op) = self.undo_stack.undo() { | 585 | self.last_point = self.idx_at_coord(pt).map(MapPoint::from); |
496 | self.apply_operation(op, OpKind::Undo); | 586 | let val = match mouse_btn { |
587 | MouseButton::Right => !self.active_color, | ||
588 | _ => self.active_color, | ||
589 | }; | ||
590 | if let Ok(o) = self.paint_point(pt, val) { | ||
591 | self.current_operation.extend(o); | ||
497 | } | 592 | } |
498 | } | 593 | } |
499 | Keycode::R => { | 594 | // click and drag |
500 | if let Some(op) = self.undo_stack.redo() { | 595 | Event::MouseMotion { |
501 | self.apply_operation(op, OpKind::Redo); | 596 | x, y, mousestate, .. |
597 | } => { | ||
598 | if mousestate.is_mouse_button_pressed(MouseButton::Left) { | ||
599 | let pt = (x, y); | ||
600 | let val = self.active_color; | ||
601 | if let Ok(o) = self.paint_point(pt, val) { | ||
602 | self.current_operation.extend(o); | ||
603 | } | ||
604 | } else if mousestate.is_mouse_button_pressed(MouseButton::Right) { | ||
605 | let pt = (x, y); | ||
606 | let val = !self.active_color; | ||
607 | if let Ok(o) = self.paint_point(pt, val) { | ||
608 | self.current_operation.extend(o); | ||
609 | } | ||
502 | } | 610 | } |
503 | } | 611 | } |
504 | _ => (), | 612 | // end of operation |
613 | Event::MouseButtonUp { .. } => { | ||
614 | let op = self | ||
615 | .current_operation | ||
616 | .drain(..) | ||
617 | .filter(|v| !v.old_val == v.val) | ||
618 | .collect::<Vec<_>>(); | ||
619 | self.undo_stack.push(op); | ||
620 | } | ||
621 | Event::Quit { .. } => { | ||
622 | break 'running; | ||
623 | } | ||
624 | _ => {} | ||
505 | } | 625 | } |
506 | } | 626 | } |
507 | // start of operation | 627 | Mode::Command => { |
508 | Event::MouseButtonDown { | 628 | if let Event::KeyDown { |
509 | x, y, mouse_btn, .. | 629 | keycode, keymod, .. |
510 | } => { | 630 | } = event |
511 | let pt = (x, y); | 631 | { |
512 | self.last_point = Some(pt.into()); | 632 | let video = self.context.video().unwrap(); |
513 | let val = match mouse_btn { | 633 | let clipboard = video.clipboard(); |
514 | MouseButton::Right => !self.active_color, | 634 | if is_copy_event(keycode, keymod) { |
515 | _ => self.active_color, | 635 | clipboard.set_clipboard_text(&self.command_box.text); |
516 | }; | 636 | } else if is_paste_event(keycode, keymod) |
517 | if let Ok(o) = self.paint_point(pt, val) { | 637 | && clipboard.has_clipboard_text() |
518 | self.current_operation.extend(o); | 638 | { |
639 | self.command_box.text = clipboard.clipboard_text().unwrap(); | ||
640 | } | ||
519 | } | 641 | } |
520 | } | 642 | if let Event::KeyDown { |
521 | // click and drag | 643 | keycode: Some(k), .. |
522 | Event::MouseMotion { | 644 | } = event |
523 | x, y, mousestate, .. | 645 | { |
524 | } => { | 646 | match k { |
525 | if mousestate.is_mouse_button_pressed(MouseButton::Left) { | 647 | Keycode::Backspace => self.command_box.backspace(), |
526 | let pt = (x, y); | 648 | Keycode::Delete => self.command_box.delete(), |
527 | let val = self.active_color; | 649 | Keycode::Left => self.command_box.backward(), |
528 | if let Ok(o) = self.paint_point(pt, val) { | 650 | Keycode::Right => self.command_box.forward(), |
529 | self.current_operation.extend(o); | 651 | Keycode::Return => self.eval_command(), |
652 | Keycode::Escape => { | ||
653 | self.command_box.clear(); | ||
654 | self.mode = Mode::Draw; | ||
655 | } | ||
656 | _ => (), | ||
530 | } | 657 | } |
531 | } else if mousestate.is_mouse_button_pressed(MouseButton::Right) { | 658 | } |
532 | let pt = (x, y); | 659 | match event { |
533 | let val = !self.active_color; | 660 | Event::TextInput { text, .. } => { |
534 | if let Ok(o) = self.paint_point(pt, val) { | 661 | self.command_box.push_str(&text[..]); |
535 | self.current_operation.extend(o); | ||
536 | } | 662 | } |
663 | _ => (), | ||
537 | } | 664 | } |
538 | } | 665 | } |
539 | // end of operation | ||
540 | Event::MouseButtonUp { .. } => { | ||
541 | let op = self | ||
542 | .current_operation | ||
543 | .drain(..) | ||
544 | .filter(|v| !v.old_val == v.val) | ||
545 | .collect::<Vec<_>>(); | ||
546 | self.undo_stack.push(op); | ||
547 | } | ||
548 | Event::Quit { .. } => { | ||
549 | break 'running; | ||
550 | } | ||
551 | _ => {} | ||
552 | } | 666 | } |
553 | } | 667 | } |
554 | self.redraw(); | 668 | self.redraw(); |
diff --git a/src/command.rs b/src/command.rs new file mode 100644 index 0000000..3beb700 --- /dev/null +++ b/src/command.rs | |||
@@ -0,0 +1,134 @@ | |||
1 | #[derive(Debug)] | ||
2 | pub struct CommandBox { | ||
3 | pub enabled: bool, | ||
4 | pub history: Vec<String>, | ||
5 | pub text: String, | ||
6 | pub cursor: usize, | ||
7 | } | ||
8 | |||
9 | // cursor value of 0 is behind all text | ||
10 | // cursor value of n is after n characters (insert after index n - 1) | ||
11 | // cursor value of text.len() is after all text | ||
12 | |||
13 | impl CommandBox { | ||
14 | pub fn new() -> Self { | ||
15 | CommandBox { | ||
16 | enabled: false, | ||
17 | history: vec![], | ||
18 | text: String::new(), | ||
19 | cursor: 0, | ||
20 | } | ||
21 | } | ||
22 | |||
23 | pub fn forward(&mut self) { | ||
24 | if self.cursor < self.text.len() { | ||
25 | self.cursor += 1; | ||
26 | } | ||
27 | } | ||
28 | |||
29 | pub fn backward(&mut self) { | ||
30 | self.cursor = self.cursor.saturating_sub(1); | ||
31 | } | ||
32 | |||
33 | pub fn backspace(&mut self) { | ||
34 | if self.cursor != 0 { | ||
35 | self.text.remove(self.cursor - 1); | ||
36 | self.backward(); | ||
37 | } | ||
38 | } | ||
39 | |||
40 | pub fn delete(&mut self) { | ||
41 | if self.cursor < self.text.len() { | ||
42 | self.text.remove(self.cursor); | ||
43 | } | ||
44 | } | ||
45 | |||
46 | pub fn push_str(&mut self, v: &str) { | ||
47 | self.text.push_str(v); | ||
48 | self.cursor += v.len(); | ||
49 | } | ||
50 | |||
51 | pub fn is_empty(&self) -> bool { | ||
52 | self.text.is_empty() | ||
53 | } | ||
54 | |||
55 | pub fn clear(&mut self) { | ||
56 | self.text.clear(); | ||
57 | self.cursor = 0; | ||
58 | } | ||
59 | } | ||
60 | |||
61 | #[cfg(test)] | ||
62 | mod tests { | ||
63 | use super::*; | ||
64 | |||
65 | fn setup_with(text: &str) -> CommandBox { | ||
66 | let mut cmd = CommandBox::new(); | ||
67 | cmd.push_str(text); | ||
68 | cmd | ||
69 | } | ||
70 | |||
71 | #[test] | ||
72 | fn entering_text() { | ||
73 | let cmd = setup_with("save as file.png"); | ||
74 | assert_eq!(&cmd.text, "save as file.png"); | ||
75 | assert_eq!(cmd.cursor, 16) | ||
76 | } | ||
77 | |||
78 | #[test] | ||
79 | fn backspacing_from_end() { | ||
80 | let mut cmd = setup_with("save"); | ||
81 | cmd.backspace(); | ||
82 | assert_eq!(&cmd.text, "sav"); | ||
83 | assert_eq!(cmd.cursor, 3); | ||
84 | } | ||
85 | |||
86 | #[test] | ||
87 | fn backspacing_from_middle() { | ||
88 | let mut cmd = setup_with("save"); | ||
89 | cmd.backward(); | ||
90 | cmd.backspace(); | ||
91 | assert_eq!(&cmd.text, "sae"); | ||
92 | assert_eq!(cmd.cursor, 2); | ||
93 | } | ||
94 | |||
95 | #[test] | ||
96 | fn delete() { | ||
97 | let mut cmd = setup_with("save"); | ||
98 | cmd.backward(); | ||
99 | cmd.delete(); | ||
100 | assert_eq!(&cmd.text, "sav"); | ||
101 | assert_eq!(cmd.cursor, 3); | ||
102 | } | ||
103 | |||
104 | #[test] | ||
105 | fn delete_end() { | ||
106 | let mut cmd = setup_with("save"); | ||
107 | cmd.delete(); | ||
108 | assert_eq!(&cmd.text, "save"); | ||
109 | } | ||
110 | |||
111 | #[test] | ||
112 | fn delete_all() { | ||
113 | let mut cmd = setup_with("save"); | ||
114 | for _ in 0..4 { | ||
115 | cmd.backward(); | ||
116 | } | ||
117 | for _ in 0..4 { | ||
118 | cmd.delete(); | ||
119 | } | ||
120 | assert_eq!(&cmd.text, ""); | ||
121 | assert_eq!(cmd.cursor, 0); | ||
122 | } | ||
123 | |||
124 | #[test] | ||
125 | fn seeking() { | ||
126 | let mut cmd = setup_with("save"); | ||
127 | for _ in 0..4 { | ||
128 | cmd.backward(); | ||
129 | } | ||
130 | assert_eq!(cmd.cursor, 0); | ||
131 | cmd.forward(); | ||
132 | assert_eq!(cmd.cursor, 1); | ||
133 | } | ||
134 | } | ||
diff --git a/src/main.rs b/src/main.rs index 14f4cf5..ebdf793 100644 --- a/src/main.rs +++ b/src/main.rs | |||
@@ -1,5 +1,6 @@ | |||
1 | mod app; | 1 | mod app; |
2 | mod bitmap; | 2 | mod bitmap; |
3 | mod command; | ||
3 | mod consts; | 4 | mod consts; |
4 | mod dither; | 5 | mod dither; |
5 | mod symmetry; | 6 | mod symmetry; |
@@ -8,8 +9,41 @@ mod utils; | |||
8 | 9 | ||
9 | use app::AppState; | 10 | use app::AppState; |
10 | 11 | ||
12 | use std::{ | ||
13 | env, | ||
14 | fs::OpenOptions, | ||
15 | io::{Cursor, Read}, | ||
16 | }; | ||
17 | |||
18 | use obi::Image; | ||
19 | |||
11 | pub fn main() { | 20 | pub fn main() { |
12 | let sdl_context = sdl2::init().unwrap(); | 21 | let sdl_context = sdl2::init().unwrap(); |
13 | let ttf_context = sdl2::ttf::init().unwrap(); | 22 | let ttf_context = sdl2::ttf::init().unwrap(); |
14 | AppState::init(200, 200, &sdl_context, &ttf_context).run(); | 23 | let args: Vec<_> = env::args().collect(); |
24 | if args.len() < 2 { | ||
25 | AppState::init(200, 200, &sdl_context, &ttf_context, None).run(); | ||
26 | return; | ||
27 | } else { | ||
28 | let path = args.get(1).unwrap(); | ||
29 | let image_src = OpenOptions::new() | ||
30 | .read(true) | ||
31 | .write(false) | ||
32 | .create(false) | ||
33 | .open(path); | ||
34 | if let Ok(mut image) = image_src { | ||
35 | let mut buf = Vec::new(); | ||
36 | image.read_to_end(&mut buf).unwrap(); | ||
37 | let decoded = Image::decode(&mut (Cursor::new(buf))).unwrap(); | ||
38 | let (width, height) = (decoded.width(), decoded.height()); | ||
39 | AppState::init( | ||
40 | width, | ||
41 | height, | ||
42 | &sdl_context, | ||
43 | &ttf_context, | ||
44 | Some(decoded.data), | ||
45 | ) | ||
46 | .run(); | ||
47 | } | ||
48 | } | ||
15 | } | 49 | } |
diff --git a/src/utils.rs b/src/utils.rs index a1b3624..71a9eea 100644 --- a/src/utils.rs +++ b/src/utils.rs | |||
@@ -1,5 +1,11 @@ | |||
1 | use crate::consts::FONT_PATH; | 1 | use crate::consts::FONT_PATH; |
2 | use sdl2::{pixels::Color, render::Canvas, ttf::Sdl2TtfContext, video::Window}; | 2 | use sdl2::{ |
3 | keyboard::{Keycode, Mod}, | ||
4 | pixels::Color, | ||
5 | render::Canvas, | ||
6 | ttf::Sdl2TtfContext, | ||
7 | video::Window, | ||
8 | }; | ||
3 | 9 | ||
4 | #[macro_export] | 10 | #[macro_export] |
5 | macro_rules! rect( | 11 | macro_rules! rect( |
@@ -27,4 +33,13 @@ pub fn draw_text<S: AsRef<str>>( | |||
27 | let (width, height) = font.size_of_latin1(text.as_bytes()).unwrap(); | 33 | let (width, height) = font.size_of_latin1(text.as_bytes()).unwrap(); |
28 | let area = rect!(x, y, width, height); | 34 | let area = rect!(x, y, width, height); |
29 | canvas.copy(&texture, None, area).unwrap(); | 35 | canvas.copy(&texture, None, area).unwrap(); |
36 | width | ||
37 | } | ||
38 | |||
39 | pub fn is_copy_event(keycode: Option<Keycode>, keymod: Mod) -> bool { | ||
40 | keycode == Some(Keycode::C) && (keymod == Mod::LCTRLMOD || keymod == Mod::RCTRLMOD) | ||
41 | } | ||
42 | |||
43 | pub fn is_paste_event(keycode: Option<Keycode>, keymod: Mod) -> bool { | ||
44 | keycode == Some(Keycode::V) && (keymod == Mod::LCTRLMOD || keymod == Mod::RCTRLMOD) | ||
30 | } | 45 | } |