1use crossterm::event::{KeyEvent, MouseEventKind};
2
3use std::{convert::TryFrom, iter::once};
4
5use crate::{box_drawing::BoxFlags, buffer::Buffer, state::State, tools::Tool};
6
7#[derive(Default)]
8pub struct Rectangle {
9 started: bool,
10 start: (usize, usize),
11 end: (usize, usize),
12 complete: bool,
13}
14
15impl Tool for Rectangle {
16 fn mouse_event(&mut self, x: isize, y: isize, kind: MouseEventKind) -> fn(state: &mut State) {
17 match kind {
18 MouseEventKind::Down(_) => {
19 if let (Ok(x), Ok(y)) = (usize::try_from(x), usize::try_from(y)) {
20 if !self.started {
21 self.start = (x, y);
22 self.end = (x, y);
23 self.started = true;
24 |_| ()
25 }
26 else {
27 self.end = (x, y);
29 self.complete = true;
30 |_| ()
31 }
32 }
33 else {
34 |_| ()
35 }
36 }
37 MouseEventKind::Drag(_) => {
38 if let (Ok(x), Ok(y)) = (usize::try_from(x), usize::try_from(y)) {
39 self.end = (x, y);
40 self.complete = true;
41 }
42 |_| ()
43 }
44 MouseEventKind::Up(_) => |state| state.reset_current_mouse_element(),
45
46 _ => |_| (),
47 }
48 }
49
50 fn key_event(&mut self, _: KeyEvent) -> fn(state: &mut State) { |_| () }
51
52 fn bounding_box(&self) -> Option<(usize, usize, usize, usize)> {
53 if self.started {
54 let (start_x, start_y) = self.start;
55 let (end_x, end_y) = self.end;
56 Some((
57 start_x.min(end_x),
58 start_x.max(end_x),
59 start_y.min(end_y),
60 start_y.max(end_y),
61 ))
62 }
63 else {
64 None
65 }
66 }
67
68 fn render(&self, buffer: &mut Buffer, ascii_mode: bool) {
69 if !self.started {
70 return;
71 }
72
73 let rect_min_x = self.start.0.min(self.end.0);
74 let rect_max_x = self.start.0.max(self.end.0);
75 let rect_min_y = self.start.1.min(self.end.1);
76 let rect_max_y = self.start.1.max(self.end.1);
77
78 let top = (rect_min_x + 1..rect_max_x).map(|x| (x, rect_min_y));
79 let bottom = (rect_min_x + 1..rect_max_x).map(|x| (x, rect_max_y));
80 let left = (rect_min_y + 1..rect_max_y).map(|y| (rect_min_x, y));
81 let right = (rect_min_y + 1..rect_max_y).map(|y| (rect_max_x, y));
82
83 let top_left = (rect_min_x, rect_min_y, BoxFlags::DOWN | BoxFlags::RIGHT);
84 let top_right = (rect_max_x, rect_min_y, BoxFlags::DOWN | BoxFlags::LEFT);
85 let bottom_left = (rect_min_x, rect_max_y, BoxFlags::UP | BoxFlags::RIGHT);
86 let bottom_right = (rect_max_x, rect_max_y, BoxFlags::UP | BoxFlags::LEFT);
87
88 top.map(|(x, y)| (x, y, BoxFlags::LEFT | BoxFlags::RIGHT))
89 .chain(bottom.map(|(x, y)| (x, y, BoxFlags::LEFT | BoxFlags::RIGHT)))
90 .chain(left.map(|(x, y)| (x, y, BoxFlags::UP | BoxFlags::DOWN)))
91 .chain(right.map(|(x, y)| (x, y, BoxFlags::UP | BoxFlags::DOWN)))
92 .chain(once(top_left))
93 .chain(once(top_right))
94 .chain(once(bottom_left))
95 .chain(once(bottom_right))
96 .for_each(|(x, y, box_dir)| {
97 let current_box = BoxFlags::from_char(buffer.get_point(x, y), ascii_mode);
98 let final_box = box_dir | current_box;
99 buffer.render_point(x, y, final_box.to_char(ascii_mode))
100 })
101 }
102
103 fn render_bounded(
104 &self,
105 min_x: usize,
106 max_x: usize,
107 min_y: usize,
108 max_y: usize,
109 buffer: &mut Buffer,
110 ascii_mode: bool,
111 ) {
112 if !self.started {
113 return;
114 }
115
116 let rect_min_x = self.start.0.min(self.end.0);
117 let rect_max_x = self.start.0.max(self.end.0);
118 let rect_min_y = self.start.1.min(self.end.1);
119 let rect_max_y = self.start.1.max(self.end.1);
120
121 let top = (rect_min_x + 1..rect_max_x).map(|x| (x, rect_min_y));
122 let bottom = (rect_min_x + 1..rect_max_x).map(|x| (x, rect_max_y));
123 let left = (rect_min_y + 1..rect_max_y).map(|y| (rect_min_x, y));
124 let right = (rect_min_y + 1..rect_max_y).map(|y| (rect_max_x, y));
125
126 let top_left = (rect_min_x, rect_min_y, BoxFlags::DOWN | BoxFlags::RIGHT);
127 let top_right = (rect_max_x, rect_min_y, BoxFlags::DOWN | BoxFlags::LEFT);
128 let bottom_left = (rect_min_x, rect_max_y, BoxFlags::UP | BoxFlags::RIGHT);
129 let bottom_right = (rect_max_x, rect_max_y, BoxFlags::UP | BoxFlags::LEFT);
130
131 top.map(|(x, y)| (x, y, BoxFlags::LEFT | BoxFlags::RIGHT))
132 .chain(bottom.map(|(x, y)| (x, y, BoxFlags::LEFT | BoxFlags::RIGHT)))
133 .chain(left.map(|(x, y)| (x, y, BoxFlags::UP | BoxFlags::DOWN)))
134 .chain(right.map(|(x, y)| (x, y, BoxFlags::UP | BoxFlags::DOWN)))
135 .chain(once(top_left))
136 .chain(once(top_right))
137 .chain(once(bottom_left))
138 .chain(once(bottom_right))
139 .filter(|(x, y, _)| (min_x <= *x && *x < max_x) && (min_y <= *y && *y < max_y))
140 .for_each(|(x, y, box_dir)| {
141 let current_box = BoxFlags::from_char(buffer.get_point(x, y), ascii_mode);
142 let final_box = box_dir | current_box;
143 buffer.render_point(x, y, final_box.to_char(ascii_mode))
144 })
145 }
146
147 fn complete(&self) -> bool { self.complete }
148}