aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/app.rs642
1 files changed, 327 insertions, 315 deletions
diff --git a/src/app.rs b/src/app.rs
index 3e7ac0f..6f39b5e 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -28,6 +28,7 @@ use std::{
28}; 28};
29 29
30use obi::{CompressionType, Image}; 30use obi::{CompressionType, Image};
31use sdl2::mouse::MouseState;
31use sdl2::{ 32use sdl2::{
32 event::Event, 33 event::Event,
33 keyboard::{Keycode, Mod}, 34 keyboard::{Keycode, Mod},
@@ -73,6 +74,11 @@ pub struct AppState<'ctx> {
73 pub pan_start: Point, 74 pub pan_start: Point,
74} 75}
75 76
77enum HandleEventOutcome {
78 Normal,
79 Break,
80}
81
76// private actions on appstate 82// private actions on appstate
77impl<'ctx> AppState<'ctx> { 83impl<'ctx> AppState<'ctx> {
78 fn pan<P: Into<Point>>(&mut self, direction: P) { 84 fn pan<P: Into<Point>>(&mut self, direction: P) {
@@ -737,354 +743,360 @@ impl<'ctx> AppState<'ctx> {
737 'running: loop { 743 'running: loop {
738 let mouse = event_pump.mouse_state(); 744 let mouse = event_pump.mouse_state();
739 self.mouse = (mouse.x(), mouse.y()); 745 self.mouse = (mouse.x(), mouse.y());
746 let event = event_pump.wait_event();
747 match self.handle_event(mouse, event) {
748 HandleEventOutcome::Normal => {}
749 HandleEventOutcome::Break => break,
750 }
740 for event in event_pump.poll_iter() { 751 for event in event_pump.poll_iter() {
741 if let Event::KeyDown { 752 match self.handle_event(mouse, event) {
742 keycode: Some(Keycode::Num9), 753 HandleEventOutcome::Normal => {}
743 keymod, 754 HandleEventOutcome::Break => break 'running,
744 ..
745 } = event
746 {
747 if keymod == Mod::LSHIFTMOD || keymod == Mod::RSHIFTMOD {
748 self.mode = Mode::Command;
749 }
750 } 755 }
751 match self.mode { 756 }
752 Mode::Draw => { 757 self.cache();
753 match event { 758 self.redraw();
754 Event::KeyDown { 759 }
755 keycode: Some(k), 760 }
756 keymod: Mod::NOMOD, 761
757 .. 762 #[must_use]
758 } => { 763 fn handle_event(&mut self, mouse: MouseState, event: Event) -> HandleEventOutcome {
759 match k { 764 if let Event::KeyDown {
760 // pan 765 keycode: Some(Keycode::Num9),
761 Keycode::W => self.pan((0, 10)), 766 keymod,
762 Keycode::A => self.pan((10, 0)), 767 ..
763 Keycode::S => self.pan((0, -10)), 768 } = event
764 Keycode::D => self.pan((-10, 0)), 769 {
765 // zoom 770 if keymod == Mod::LSHIFTMOD || keymod == Mod::RSHIFTMOD {
766 // Keycode::C if keymod == Mod::LSHIFTMOD => { 771 self.mode = Mode::Command;
767 // self.center_grid(); 772 }
768 // } 773 }
769 Keycode::C => { 774 match self.mode {
770 let cursor = (mouse.x(), mouse.y()); 775 Mode::Draw => {
771 self.zoom_in(cursor); 776 match event {
772 } 777 Event::KeyDown {
773 Keycode::Z => { 778 keycode: Some(k),
774 let cursor = (mouse.x(), mouse.y()); 779 keymod: Mod::NOMOD,
775 self.zoom_out(cursor); 780 ..
776 } 781 } => {
777 // brush ops 782 match k {
778 Keycode::Q => self.brush.shrink(), 783 // pan
779 Keycode::E => self.brush.grow(), 784 Keycode::W => self.pan((0, 10)),
780 Keycode::Num1 => self.reduce_intensity(), 785 Keycode::A => self.pan((10, 0)),
781 Keycode::Num3 => self.increase_intensity(), 786 Keycode::S => self.pan((0, -10)),
782 // flip color 787 Keycode::D => self.pan((-10, 0)),
783 Keycode::X => self.change_active_color(), 788 // zoom
784 // toggle grid 789 // Keycode::C if keymod == Mod::LSHIFTMOD => {
785 Keycode::Tab => self.toggle_grid(), 790 // self.center_grid();
786 // invert canvas 791 // }
787 Keycode::I => { 792 Keycode::C => {
788 self.pixmap.invert(); 793 let cursor = (mouse.x(), mouse.y());
789 self.undo_stack.push(ModifyRecord::Invert); 794 self.zoom_in(cursor);
790 } 795 }
791 // cycle through brushes 796 Keycode::Z => {
792 Keycode::F => self.cycle_brush(), 797 let cursor = (mouse.x(), mouse.y());
793 // change rect select active end 798 self.zoom_out(cursor);
794 Keycode::O => { 799 }
795 match &mut self.brush { 800 // brush ops
796 Brush::RectSelect(RectSelectBrush { 801 Keycode::Q => self.brush.shrink(),
797 active_end, 802 Keycode::E => self.brush.grow(),
798 .. 803 Keycode::Num1 => self.reduce_intensity(),
799 }) => *active_end = !*active_end, 804 Keycode::Num3 => self.increase_intensity(),
800 _ => (), 805 // flip color
801 }; 806 Keycode::X => self.change_active_color(),
802 } 807 // toggle grid
803 Keycode::V => self.cycle_symmetry(), 808 Keycode::Tab => self.toggle_grid(),
804 // undo 809 // invert canvas
805 Keycode::U => { 810 Keycode::I => {
806 if let Some(op) = self.undo_stack.undo() { 811 self.pixmap.invert();
807 self.apply_operation(op, OpKind::Undo); 812 self.undo_stack.push(ModifyRecord::Invert);
808 } 813 }
809 } 814 // cycle through brushes
810 // redo 815 Keycode::F => self.cycle_brush(),
811 Keycode::R => { 816 // change rect select active end
812 if let Some(op) = self.undo_stack.redo() { 817 Keycode::O => {
813 self.apply_operation(op, OpKind::Redo); 818 match &mut self.brush {
814 } 819 Brush::RectSelect(RectSelectBrush { active_end, .. }) => {
815 } 820 *active_end = !*active_end
816 Keycode::Escape => {
817 match self.brush {
818 Brush::RectSelect(_) => self.brush = Brush::rect(),
819 _ => (),
820 }
821 continue;
822 } 821 }
823 _ => (), 822 _ => (),
823 };
824 }
825 Keycode::V => self.cycle_symmetry(),
826 // undo
827 Keycode::U => {
828 if let Some(op) = self.undo_stack.undo() {
829 self.apply_operation(op, OpKind::Undo);
824 } 830 }
825 } 831 }
826 Event::KeyDown { 832 // redo
827 keycode: Some(k), 833 Keycode::R => {
828 keymod, 834 if let Some(op) = self.undo_stack.redo() {
829 .. 835 self.apply_operation(op, OpKind::Redo);
830 } if self.keybinds.contains_key(&Keybind::new(k, keymod)) => { 836 }
831 let body =
832 // clone here because body can modify itself
833 self.keybinds.get(&Keybind::new(k, keymod)).unwrap().clone();
834 self.eval_expr(&body);
835 } 837 }
836 Event::KeyDown { 838 Keycode::Escape => {
837 keycode: Some(k), .. 839 match self.brush {
838 } if k == Keycode::LCtrl || k == Keycode::RCtrl => { 840 Brush::RectSelect(_) => self.brush = Brush::rect(),
839 self.brush = Brush::line( 841 _ => (),
840 self.cache 842 }
841 .borrow() 843 return HandleEventOutcome::Normal;
842 .as_ref()
843 .map(|c| c.last_brush.size().unwrap_or(0))
844 .unwrap_or(0),
845 true,
846 );
847 } 844 }
848 Event::KeyUp { 845 _ => (),
849 keycode: Some(k), .. 846 }
850 } if k == Keycode::LCtrl || k == Keycode::RCtrl => { 847 }
851 self.brush = Brush::new( 848 Event::KeyDown {
852 self.cache 849 keycode: Some(k),
853 .borrow() 850 keymod,
854 .as_ref() 851 ..
855 .map(|c| c.last_brush.size().unwrap_or(0)) 852 } if self.keybinds.contains_key(&Keybind::new(k, keymod)) => {
856 .unwrap_or(0), 853 let body =
857 ); 854 // clone here because body can modify itself
855 self.keybinds.get(&Keybind::new(k, keymod)).unwrap().clone();
856 self.eval_expr(&body);
857 }
858 Event::KeyDown {
859 keycode: Some(k), ..
860 } if k == Keycode::LCtrl || k == Keycode::RCtrl => {
861 self.brush = Brush::line(
862 self.cache
863 .borrow()
864 .as_ref()
865 .map(|c| c.last_brush.size().unwrap_or(0))
866 .unwrap_or(0),
867 true,
868 );
869 }
870 Event::KeyUp {
871 keycode: Some(k), ..
872 } if k == Keycode::LCtrl || k == Keycode::RCtrl => {
873 self.brush = Brush::new(
874 self.cache
875 .borrow()
876 .as_ref()
877 .map(|c| c.last_brush.size().unwrap_or(0))
878 .unwrap_or(0),
879 );
880 }
881 Event::MouseButtonDown {
882 x,
883 y,
884 mouse_btn: MouseButton::Middle,
885 ..
886 } => self.pan_start = Point::from((x, y)),
887 // start of operation
888 Event::MouseButtonDown {
889 x, y, mouse_btn, ..
890 } => {
891 let pt = (x, y);
892 let contact = self.idx_at_coord(pt).map(MapPoint::from);
893 let val = match mouse_btn {
894 MouseButton::Right => !self.active_color,
895 _ => self.active_color,
896 };
897 match self.brush {
898 Brush::Circle(CircleBrush { size }) => {
899 if let Ok(o) = self.paint_point(pt, val, size) {
900 self.current_operation.extend(o);
901 }
858 } 902 }
859 Event::MouseButtonDown { 903 Brush::Line(LineBrush {
860 x, 904 size,
861 y, 905 start,
862 mouse_btn: MouseButton::Middle, 906 extend,
863 .. 907 }) => {
864 } => self.pan_start = Point::from((x, y)), 908 if let Some(s) = start {
865 // start of operation 909 if let Ok(o) = self.paint_line(s, pt, val, size) {
866 Event::MouseButtonDown { 910 self.current_operation.extend(o);
867 x, y, mouse_btn, .. 911 self.brush = Brush::Line(LineBrush {
868 } => { 912 size,
869 let pt = (x, y); 913 start: if extend { contact } else { None },
870 let contact = self.idx_at_coord(pt).map(MapPoint::from); 914 extend,
871 let val = match mouse_btn { 915 });
872 MouseButton::Right => !self.active_color,
873 _ => self.active_color,
874 };
875 match self.brush {
876 Brush::Circle(CircleBrush { size }) => {
877 if let Ok(o) = self.paint_point(pt, val, size) {
878 self.current_operation.extend(o);
879 }
880 } 916 }
881 Brush::Line(LineBrush { 917 } else {
918 self.brush = Brush::Line(LineBrush {
882 size, 919 size,
883 start, 920 start: contact,
884 extend, 921 extend,
885 }) => { 922 });
886 if let Some(s) = start { 923 }
887 if let Ok(o) = self.paint_line(s, pt, val, size) { 924 }
888 self.current_operation.extend(o); 925 Brush::RectSelect(RectSelectBrush {
889 self.brush = Brush::Line(LineBrush { 926 start,
890 size, 927 end,
891 start: if extend { contact } else { None }, 928 active_end,
892 extend, 929 }) => {
893 }); 930 if start.is_none() {
894 } 931 self.brush = Brush::RectSelect(RectSelectBrush {
895 } else { 932 start: contact,
896 self.brush = Brush::Line(LineBrush { 933 end,
897 size, 934 active_end,
898 start: contact, 935 });
899 extend, 936 } else if end.is_none() || active_end {
900 }); 937 self.brush = Brush::RectSelect(RectSelectBrush {
901 }
902 }
903 Brush::RectSelect(RectSelectBrush {
904 start, 938 start,
939 end: contact,
940 active_end,
941 });
942 } else {
943 self.brush = Brush::RectSelect(RectSelectBrush {
944 start: contact,
905 end, 945 end,
906 active_end, 946 active_end,
907 }) => { 947 });
908 if start.is_none() { 948 };
909 self.brush = Brush::RectSelect(RectSelectBrush { 949 }
910 start: contact, 950 Brush::Fill => {
911 end, 951 if let Some(c) = contact {
912 active_end, 952 // this `get` is unchecked because contact is checked
913 }); 953 // to be within pixmap
914 } else if end.is_none() || active_end { 954 let target = self.pixmap.get(c);
915 self.brush = Brush::RectSelect(RectSelectBrush { 955 let replacement = self.active_color;
916 start, 956 let operation = self.pixmap.flood_fill(c, target, replacement);
917 end: contact, 957 for o in operation.iter() {
918 active_end, 958 // this `set` is unchecked because the returned
919 }); 959 // value of flood_fill is checked to be within pixmap
920 } else { 960 self.pixmap.set(*o, replacement);
921 self.brush = Brush::RectSelect(RectSelectBrush {
922 start: contact,
923 end,
924 active_end,
925 });
926 };
927 }
928 Brush::Fill => {
929 if let Some(c) = contact {
930 // this `get` is unchecked because contact is checked
931 // to be within pixmap
932 let target = self.pixmap.get(c);
933 let replacement = self.active_color;
934 let operation =
935 self.pixmap.flood_fill(c, target, replacement);
936 for o in operation.iter() {
937 // this `set` is unchecked because the returned
938 // value of flood_fill is checked to be within pixmap
939 self.pixmap.set(*o, replacement);
940 }
941 self.current_operation.extend(
942 operation
943 .into_iter()
944 .map(|point| PaintRecord {
945 point,
946 old: target,
947 new: replacement,
948 })
949 .collect::<Vec<PaintRecord>>(),
950 )
951 }
952 } 961 }
953 _ => {} 962 self.current_operation.extend(
963 operation
964 .into_iter()
965 .map(|point| PaintRecord {
966 point,
967 old: target,
968 new: replacement,
969 })
970 .collect::<Vec<PaintRecord>>(),
971 )
954 } 972 }
955 } 973 }
956 // click and drag 974 _ => {}
957 Event::MouseMotion { 975 }
958 x, y, mousestate, .. 976 }
959 } => { 977 // click and drag
960 if mousestate.is_mouse_button_pressed(MouseButton::Left) { 978 Event::MouseMotion {
961 match self.brush { 979 x, y, mousestate, ..
962 Brush::RectSelect(RectSelectBrush { 980 } => {
981 if mousestate.is_mouse_button_pressed(MouseButton::Left) {
982 match self.brush {
983 Brush::RectSelect(RectSelectBrush {
984 start,
985 end,
986 active_end,
987 }) => {
988 if active_end {
989 self.brush = Brush::RectSelect(RectSelectBrush {
963 start, 990 start,
991 end: self.idx_at_coord((x, y)).map(MapPoint::from),
992 active_end,
993 });
994 } else {
995 self.brush = Brush::RectSelect(RectSelectBrush {
996 start: self.idx_at_coord((x, y)).map(MapPoint::from),
964 end, 997 end,
965 active_end, 998 active_end,
966 }) => { 999 });
967 if active_end {
968 self.brush = Brush::RectSelect(RectSelectBrush {
969 start,
970 end: self
971 .idx_at_coord((x, y))
972 .map(MapPoint::from),
973 active_end,
974 });
975 } else {
976 self.brush = Brush::RectSelect(RectSelectBrush {
977 start: self
978 .idx_at_coord((x, y))
979 .map(MapPoint::from),
980 end,
981 active_end,
982 });
983 }
984 }
985 Brush::Circle(CircleBrush { size }) => {
986 let pt = (x, y);
987 let val = self.active_color;
988 if let Ok(o) = self.paint_point(pt, val, size) {
989 self.current_operation.extend(o);
990 }
991 }
992 _ => (),
993 } 1000 }
994 } else if mousestate.is_mouse_button_pressed(MouseButton::Right) { 1001 }
995 match self.brush { 1002 Brush::Circle(CircleBrush { size }) => {
996 Brush::Circle(CircleBrush { size }) 1003 let pt = (x, y);
997 | Brush::Line(LineBrush { size, .. }) => { 1004 let val = self.active_color;
998 let pt = (x, y); 1005 if let Ok(o) = self.paint_point(pt, val, size) {
999 let val = !self.active_color; 1006 self.current_operation.extend(o);
1000 if let Ok(o) = self.paint_point(pt, val, size) {
1001 self.current_operation.extend(o);
1002 }
1003 }
1004 _ => (),
1005 } 1007 }
1006 } else if mousestate.is_mouse_button_pressed(MouseButton::Middle) {
1007 let pan_end = Point::from((x, y));
1008 let shift = pan_end - self.pan_start;
1009 self.start += shift;
1010 self.pan_start = pan_end;
1011 } 1008 }
1009 _ => (),
1012 } 1010 }
1013 // end of operation 1011 } else if mousestate.is_mouse_button_pressed(MouseButton::Right) {
1014 Event::MouseButtonUp { .. } => self.commit_operation(), 1012 match self.brush {
1015 Event::Quit { .. } => { 1013 Brush::Circle(CircleBrush { size })
1016 break 'running; 1014 | Brush::Line(LineBrush { size, .. }) => {
1015 let pt = (x, y);
1016 let val = !self.active_color;
1017 if let Ok(o) = self.paint_point(pt, val, size) {
1018 self.current_operation.extend(o);
1019 }
1020 }
1021 _ => (),
1017 } 1022 }
1018 _ => {} 1023 } else if mousestate.is_mouse_button_pressed(MouseButton::Middle) {
1024 let pan_end = Point::from((x, y));
1025 let shift = pan_end - self.pan_start;
1026 self.start += shift;
1027 self.pan_start = pan_end;
1019 } 1028 }
1020 } 1029 }
1021 Mode::Command => { 1030 // end of operation
1022 if let Event::KeyDown { 1031 Event::MouseButtonUp { .. } => self.commit_operation(),
1023 keycode, keymod, .. 1032 Event::Quit { .. } => {
1024 } = event 1033 return HandleEventOutcome::Break;
1025 { 1034 }
1026 let video = self.context.video().unwrap(); 1035 _ => {}
1027 let clipboard = video.clipboard(); 1036 }
1028 if is_copy_event(keycode, keymod) { 1037 }
1029 clipboard 1038 Mode::Command => {
1030 .set_clipboard_text(&self.command_box.text) 1039 if let Event::KeyDown {
1031 .unwrap(); 1040 keycode, keymod, ..
1032 } else if is_paste_event(keycode, keymod) 1041 } = event
1033 && clipboard.has_clipboard_text() 1042 {
1034 { 1043 let video = self.context.video().unwrap();
1035 self.command_box 1044 let clipboard = video.clipboard();
1036 .push_str(&clipboard.clipboard_text().unwrap()); 1045 if is_copy_event(keycode, keymod) {
1037 } 1046 clipboard
1047 .set_clipboard_text(&self.command_box.text)
1048 .unwrap();
1049 } else if is_paste_event(keycode, keymod) && clipboard.has_clipboard_text() {
1050 self.command_box
1051 .push_str(&clipboard.clipboard_text().unwrap());
1052 }
1053 }
1054 match event {
1055 Event::KeyDown {
1056 keycode: Some(k),
1057 keymod,
1058 ..
1059 } => match k {
1060 Keycode::Backspace => self.command_box.backspace(),
1061 Keycode::Delete => self.command_box.delete(),
1062 Keycode::Left => self.command_box.backward(),
1063 Keycode::Right => self.command_box.forward(),
1064 Keycode::Up => self.command_box.hist_prev(),
1065 Keycode::Down => self.command_box.hist_next(),
1066 Keycode::Return => self.eval_command(),
1067 Keycode::Tab if keymod == Mod::LSHIFTMOD => {
1068 self.command_box.complete(&self.lisp_env, true)
1038 } 1069 }
1039 match event { 1070 Keycode::Tab => self.command_box.complete(&self.lisp_env, false),
1040 Event::KeyDown { 1071 Keycode::Escape => {
1041 keycode: Some(k), 1072 self.command_box.clear();
1042 keymod, 1073 self.message.text.clear();
1043 .. 1074 self.mode = Mode::Draw;
1044 } => match k {
1045 Keycode::Backspace => self.command_box.backspace(),
1046 Keycode::Delete => self.command_box.delete(),
1047 Keycode::Left => self.command_box.backward(),
1048 Keycode::Right => self.command_box.forward(),
1049 Keycode::Up => self.command_box.hist_prev(),
1050 Keycode::Down => self.command_box.hist_next(),
1051 Keycode::Return => self.eval_command(),
1052 Keycode::Tab if keymod == Mod::LSHIFTMOD => {
1053 self.command_box.complete(&self.lisp_env, true)
1054 }
1055 Keycode::Tab => self.command_box.complete(&self.lisp_env, false),
1056 Keycode::Escape => {
1057 self.command_box.clear();
1058 self.message.text.clear();
1059 self.mode = Mode::Draw;
1060 }
1061 _ if keymod == Mod::LCTRLMOD => match k {
1062 Keycode::A => self.command_box.cursor_start(),
1063 Keycode::E => self.command_box.cursor_end(),
1064 Keycode::F => self.command_box.forward(),
1065 Keycode::B => self.command_box.backward(),
1066 Keycode::K => self.command_box.delete_to_end(),
1067 Keycode::U => self.command_box.delete_to_start(),
1068 _ => (),
1069 },
1070 // how does one handle alt keys
1071 _ if keymod == Mod::LALTMOD => match k {
1072 Keycode::B => self.command_box.cursor_back_word(),
1073 Keycode::F => self.command_box.cursor_forward_word(),
1074 _ => (),
1075 },
1076 _ => (),
1077 },
1078 Event::TextInput { text, .. } => {
1079 self.command_box.push_str(&text[..]);
1080 }
1081 _ => (),
1082 } 1075 }
1076 _ if keymod == Mod::LCTRLMOD => match k {
1077 Keycode::A => self.command_box.cursor_start(),
1078 Keycode::E => self.command_box.cursor_end(),
1079 Keycode::F => self.command_box.forward(),
1080 Keycode::B => self.command_box.backward(),
1081 Keycode::K => self.command_box.delete_to_end(),
1082 Keycode::U => self.command_box.delete_to_start(),
1083 _ => (),
1084 },
1085 // how does one handle alt keys
1086 _ if keymod == Mod::LALTMOD => match k {
1087 Keycode::B => self.command_box.cursor_back_word(),
1088 Keycode::F => self.command_box.cursor_forward_word(),
1089 _ => (),
1090 },
1091 _ => (),
1092 },
1093 Event::TextInput { text, .. } => {
1094 self.command_box.push_str(&text[..]);
1083 } 1095 }
1096 _ => (),
1084 } 1097 }
1085 } 1098 }
1086 self.cache();
1087 self.redraw();
1088 } 1099 }
1100 HandleEventOutcome::Normal
1089 } 1101 }
1090} 1102}