1use std::collections::{HashMap, HashSet};
4use kurbo::Point;
5use crate::shapes::{Shape, ShapeId, ShapeTrait};
6use super::state::{WidgetState, EditingKind};
7use super::handles::{Handle, HandleKind, HandleShape};
8
9#[derive(Debug, Clone)]
14pub struct WidgetManager {
15 states: HashMap<ShapeId, WidgetState>,
17 selected: HashSet<ShapeId>,
19 focused: Option<ShapeId>,
21 hovered: Option<ShapeId>,
23}
24
25impl WidgetManager {
26 pub fn new() -> Self {
28 Self {
29 states: HashMap::new(),
30 selected: HashSet::new(),
31 focused: None,
32 hovered: None,
33 }
34 }
35
36 pub fn state(&self, id: ShapeId) -> WidgetState {
38 self.states.get(&id).cloned().unwrap_or_default()
39 }
40
41 pub fn set_state(&mut self, id: ShapeId, state: WidgetState) {
43 if state.is_selected() {
45 self.selected.insert(id);
46 } else {
47 self.selected.remove(&id);
48 }
49
50 if state.is_editing() {
52 self.focused = Some(id);
53 } else if self.focused == Some(id) {
54 self.focused = None;
55 }
56
57 self.states.insert(id, state);
58 }
59
60 pub fn is_selected(&self, id: ShapeId) -> bool {
62 self.selected.contains(&id)
63 }
64
65 pub fn selected(&self) -> &HashSet<ShapeId> {
67 &self.selected
68 }
69
70 pub fn focused(&self) -> Option<ShapeId> {
72 self.focused
73 }
74
75 pub fn hovered(&self) -> Option<ShapeId> {
77 self.hovered
78 }
79
80 pub fn set_hovered(&mut self, id: Option<ShapeId>) {
82 if let Some(old_id) = self.hovered {
84 if Some(old_id) != id {
85 if let Some(state) = self.states.get(&old_id) {
86 if *state == WidgetState::Hovered {
87 self.states.insert(old_id, WidgetState::Normal);
88 }
89 }
90 }
91 }
92
93 if let Some(new_id) = id {
95 let current = self.state(new_id);
96 if current == WidgetState::Normal {
97 self.states.insert(new_id, WidgetState::Hovered);
98 }
99 }
100
101 self.hovered = id;
102 }
103
104 pub fn select(&mut self, id: ShapeId) {
106 self.clear_selection();
107 self.add_to_selection(id);
108 }
109
110 pub fn add_to_selection(&mut self, id: ShapeId) {
112 self.set_state(id, WidgetState::Selected);
113 }
114
115 pub fn deselect(&mut self, id: ShapeId) {
117 if self.selected.contains(&id) {
118 self.set_state(id, WidgetState::Normal);
119 }
120 }
121
122 pub fn clear_selection(&mut self) {
124 let selected: Vec<_> = self.selected.iter().copied().collect();
125 for id in selected {
126 self.set_state(id, WidgetState::Normal);
127 }
128 self.selected.clear();
129 }
130
131 pub fn enter_editing(&mut self, id: ShapeId, kind: EditingKind) {
133 if let Some(old_id) = self.focused {
135 if old_id != id {
136 self.exit_editing();
137 }
138 }
139
140 self.set_state(id, WidgetState::Editing(kind));
141 }
142
143 pub fn exit_editing(&mut self) {
145 if let Some(id) = self.focused {
146 self.set_state(id, WidgetState::Selected);
147 }
148 }
149
150 pub fn is_editing(&self) -> bool {
152 self.focused.is_some()
153 }
154
155 pub fn is_editing_shape(&self, id: ShapeId) -> bool {
157 self.focused == Some(id)
158 }
159
160 pub fn remove(&mut self, id: ShapeId) {
162 self.states.remove(&id);
163 self.selected.remove(&id);
164 if self.focused == Some(id) {
165 self.focused = None;
166 }
167 if self.hovered == Some(id) {
168 self.hovered = None;
169 }
170 }
171
172 pub fn get_handles(&self, shape: &Shape) -> Vec<Handle> {
174 let id = shape.id();
175 if !self.is_selected(id) {
176 return vec![];
177 }
178
179 get_shape_handles(shape)
180 }
181}
182
183impl Default for WidgetManager {
184 fn default() -> Self {
185 Self::new()
186 }
187}
188
189pub fn get_shape_handles(shape: &Shape) -> Vec<Handle> {
191 match shape {
192 Shape::Rectangle(r) => {
193 let bounds = r.bounds();
194 vec![
195 Handle::new(HandleKind::TopLeft, Point::new(bounds.x0, bounds.y0)),
196 Handle::new(HandleKind::TopRight, Point::new(bounds.x1, bounds.y0)),
197 Handle::new(HandleKind::BottomLeft, Point::new(bounds.x0, bounds.y1)),
198 Handle::new(HandleKind::BottomRight, Point::new(bounds.x1, bounds.y1)),
199 ]
200 }
201 Shape::Ellipse(e) => {
202 let bounds = e.bounds();
203 vec![
204 Handle::new(HandleKind::TopLeft, Point::new(bounds.x0, bounds.y0)),
205 Handle::new(HandleKind::TopRight, Point::new(bounds.x1, bounds.y0)),
206 Handle::new(HandleKind::BottomLeft, Point::new(bounds.x0, bounds.y1)),
207 Handle::new(HandleKind::BottomRight, Point::new(bounds.x1, bounds.y1)),
208 ]
209 }
210 Shape::Line(l) => {
211 vec![
212 Handle::new(HandleKind::Start, l.start).with_shape(HandleShape::Circle),
213 Handle::new(HandleKind::End, l.end).with_shape(HandleShape::Circle),
214 ]
215 }
216 Shape::Arrow(a) => {
217 vec![
218 Handle::new(HandleKind::Start, a.start).with_shape(HandleShape::Circle),
219 Handle::new(HandleKind::End, a.end).with_shape(HandleShape::Circle),
220 ]
221 }
222 Shape::Freehand(f) => {
223 let bounds = f.bounds();
224 vec![
225 Handle::new(HandleKind::TopLeft, Point::new(bounds.x0, bounds.y0)),
226 Handle::new(HandleKind::TopRight, Point::new(bounds.x1, bounds.y0)),
227 Handle::new(HandleKind::BottomLeft, Point::new(bounds.x0, bounds.y1)),
228 Handle::new(HandleKind::BottomRight, Point::new(bounds.x1, bounds.y1)),
229 ]
230 }
231 Shape::Text(t) => {
232 let bounds = t.bounds();
233 vec![
234 Handle::new(HandleKind::TopLeft, Point::new(bounds.x0, bounds.y0)),
235 Handle::new(HandleKind::TopRight, Point::new(bounds.x1, bounds.y0)),
236 Handle::new(HandleKind::BottomLeft, Point::new(bounds.x0, bounds.y1)),
237 Handle::new(HandleKind::BottomRight, Point::new(bounds.x1, bounds.y1)),
238 ]
239 }
240 Shape::Group(g) => {
241 let bounds = g.bounds();
243 vec![
244 Handle::new(HandleKind::TopLeft, Point::new(bounds.x0, bounds.y0)),
245 Handle::new(HandleKind::TopRight, Point::new(bounds.x1, bounds.y0)),
246 Handle::new(HandleKind::BottomLeft, Point::new(bounds.x0, bounds.y1)),
247 Handle::new(HandleKind::BottomRight, Point::new(bounds.x1, bounds.y1)),
248 ]
249 }
250 Shape::Image(img) => {
251 let bounds = img.bounds();
253 vec![
254 Handle::new(HandleKind::TopLeft, Point::new(bounds.x0, bounds.y0)),
255 Handle::new(HandleKind::TopRight, Point::new(bounds.x1, bounds.y0)),
256 Handle::new(HandleKind::BottomLeft, Point::new(bounds.x0, bounds.y1)),
257 Handle::new(HandleKind::BottomRight, Point::new(bounds.x1, bounds.y1)),
258 ]
259 }
260 }
261}
262
263#[allow(dead_code)]
265pub fn hit_test_handle(shape: &Shape, point: Point, tolerance: f64) -> Option<HandleKind> {
266 let handles = get_shape_handles(shape);
267 for handle in handles {
268 let dx = point.x - handle.position.x;
269 let dy = point.y - handle.position.y;
270 if dx * dx + dy * dy <= tolerance * tolerance {
271 return Some(handle.kind);
272 }
273 }
274 None
275}