1use crate::geometry::{Point, Rect};
10use crate::widget::WidgetId;
11use std::any::Any;
12use std::collections::HashMap;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
16pub struct DragId(pub u64);
17
18impl DragId {
19 pub const fn new(id: u64) -> Self {
21 Self(id)
22 }
23}
24
25#[derive(Debug, Clone, PartialEq, Eq, Hash)]
27pub enum DragDataType {
28 Text,
30 Html,
32 Url,
34 File,
36 Custom(String),
38}
39
40impl DragDataType {
41 pub fn custom(name: &str) -> Self {
43 Self::Custom(name.to_string())
44 }
45}
46
47#[derive(Debug, Clone)]
49pub struct DragData {
50 pub data_type: DragDataType,
52 pub text: String,
54 pub formats: HashMap<DragDataType, String>,
56 pub payload: Option<DragPayload>,
58}
59
60impl DragData {
61 pub fn text(content: &str) -> Self {
63 Self {
64 data_type: DragDataType::Text,
65 text: content.to_string(),
66 formats: HashMap::new(),
67 payload: None,
68 }
69 }
70
71 pub fn html(content: &str) -> Self {
73 let mut formats = HashMap::new();
74 formats.insert(DragDataType::Html, content.to_string());
75 Self {
76 data_type: DragDataType::Html,
77 text: content.to_string(),
78 formats,
79 payload: None,
80 }
81 }
82
83 pub fn url(url: &str) -> Self {
85 Self {
86 data_type: DragDataType::Url,
87 text: url.to_string(),
88 formats: HashMap::new(),
89 payload: None,
90 }
91 }
92
93 pub fn custom(type_name: &str, data: &str) -> Self {
95 Self {
96 data_type: DragDataType::Custom(type_name.to_string()),
97 text: data.to_string(),
98 formats: HashMap::new(),
99 payload: None,
100 }
101 }
102
103 pub fn with_format(mut self, data_type: DragDataType, data: &str) -> Self {
105 self.formats.insert(data_type, data.to_string());
106 self
107 }
108
109 pub fn with_payload<T: Any + Send + Sync + Clone + 'static>(mut self, payload: T) -> Self {
111 self.payload = Some(DragPayload::new(payload));
112 self
113 }
114
115 pub fn get_format(&self, data_type: &DragDataType) -> Option<&str> {
117 if &self.data_type == data_type {
118 Some(&self.text)
119 } else {
120 self.formats.get(data_type).map(std::string::String::as_str)
121 }
122 }
123
124 pub fn has_format(&self, data_type: &DragDataType) -> bool {
126 &self.data_type == data_type || self.formats.contains_key(data_type)
127 }
128}
129
130#[derive(Debug, Clone)]
132pub struct DragPayload {
133 data: Box<dyn CloneableAny>,
134}
135
136impl DragPayload {
137 pub fn new<T: Any + Send + Sync + Clone + 'static>(data: T) -> Self {
139 Self {
140 data: Box::new(data),
141 }
142 }
143
144 pub fn get<T: Any + Send + Sync + Clone + 'static>(&self) -> Option<&T> {
146 self.data.as_any().downcast_ref()
147 }
148}
149
150trait CloneableAny: Any + Send + Sync {
152 fn clone_box(&self) -> Box<dyn CloneableAny>;
153 fn as_any(&self) -> &dyn Any;
154}
155
156impl<T: Any + Send + Sync + Clone + 'static> CloneableAny for T {
157 fn clone_box(&self) -> Box<dyn CloneableAny> {
158 Box::new(self.clone())
159 }
160
161 fn as_any(&self) -> &dyn Any {
162 self
163 }
164}
165
166impl Clone for Box<dyn CloneableAny> {
167 fn clone(&self) -> Self {
168 self.clone_box()
169 }
170}
171
172impl std::fmt::Debug for Box<dyn CloneableAny> {
173 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
174 f.debug_struct("CloneableAny").finish_non_exhaustive()
175 }
176}
177
178#[derive(Debug, Clone, Copy, PartialEq, Eq)]
180pub enum DragPhase {
181 Started,
183 Dragging,
185 OverTarget,
187 Dropped,
189 Cancelled,
191}
192
193#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
195pub enum DropEffect {
196 #[default]
198 None,
199 Copy,
201 Move,
203 Link,
205}
206
207#[derive(Debug, Clone)]
209pub struct DragState {
210 pub id: DragId,
212 pub source_widget: WidgetId,
214 pub phase: DragPhase,
216 pub start_position: Point,
218 pub current_position: Point,
220 pub data: DragData,
222 pub hover_target: Option<WidgetId>,
224 pub allowed_effects: Vec<DropEffect>,
226 pub effect: DropEffect,
228}
229
230impl DragState {
231 pub fn new(id: DragId, source_widget: WidgetId, position: Point, data: DragData) -> Self {
233 Self {
234 id,
235 source_widget,
236 phase: DragPhase::Started,
237 start_position: position,
238 current_position: position,
239 data,
240 hover_target: None,
241 allowed_effects: vec![DropEffect::Copy, DropEffect::Move],
242 effect: DropEffect::None,
243 }
244 }
245
246 pub fn offset(&self) -> Point {
248 self.current_position - self.start_position
249 }
250
251 pub fn is_active(&self) -> bool {
253 matches!(
254 self.phase,
255 DragPhase::Started | DragPhase::Dragging | DragPhase::OverTarget
256 )
257 }
258}
259
260#[derive(Debug, Clone)]
262pub struct DropTarget {
263 pub widget_id: WidgetId,
265 pub accepted_types: Vec<DragDataType>,
267 pub accepted_effects: Vec<DropEffect>,
269 pub bounds: Rect,
271 pub enabled: bool,
273}
274
275impl DropTarget {
276 pub fn new(widget_id: WidgetId, bounds: Rect) -> Self {
278 Self {
279 widget_id,
280 accepted_types: vec![],
281 accepted_effects: vec![DropEffect::Copy, DropEffect::Move],
282 bounds,
283 enabled: true,
284 }
285 }
286
287 pub fn accept_types(mut self, types: Vec<DragDataType>) -> Self {
289 self.accepted_types = types;
290 self
291 }
292
293 pub fn accept_effects(mut self, effects: Vec<DropEffect>) -> Self {
295 self.accepted_effects = effects;
296 self
297 }
298
299 pub fn accepts(&self, data: &DragData, effect: DropEffect) -> bool {
301 if !self.enabled {
302 return false;
303 }
304
305 if !self.accepted_effects.contains(&effect) {
307 return false;
308 }
309
310 if self.accepted_types.is_empty() {
312 return true;
313 }
314
315 self.accepted_types.contains(&data.data_type)
316 || self.accepted_types.iter().any(|t| data.has_format(t))
317 }
318
319 pub fn contains_point(&self, point: Point) -> bool {
321 self.enabled && self.bounds.contains_point(&point)
322 }
323}
324
325#[derive(Debug, Clone)]
327pub struct DropResult {
328 pub success: bool,
330 pub target: WidgetId,
332 pub effect: DropEffect,
334 pub position: Point,
336}
337
338pub struct DragDropManager {
340 next_id: u64,
342 current_drag: Option<DragState>,
344 targets: HashMap<WidgetId, DropTarget>,
346 preview_offset: Point,
348 min_drag_distance: f32,
350}
351
352impl DragDropManager {
353 pub fn new() -> Self {
355 Self {
356 next_id: 0,
357 current_drag: None,
358 targets: HashMap::new(),
359 preview_offset: Point::ORIGIN,
360 min_drag_distance: 5.0,
361 }
362 }
363
364 pub fn set_min_drag_distance(&mut self, distance: f32) {
366 self.min_drag_distance = distance;
367 }
368
369 pub fn set_preview_offset(&mut self, offset: Point) {
371 self.preview_offset = offset;
372 }
373
374 pub fn register_target(&mut self, target: DropTarget) {
376 self.targets.insert(target.widget_id, target);
377 }
378
379 pub fn unregister_target(&mut self, widget_id: WidgetId) {
381 self.targets.remove(&widget_id);
382 }
383
384 pub fn update_target_bounds(&mut self, widget_id: WidgetId, bounds: Rect) {
386 if let Some(target) = self.targets.get_mut(&widget_id) {
387 target.bounds = bounds;
388 }
389 }
390
391 pub fn start_drag(
393 &mut self,
394 source_widget: WidgetId,
395 position: Point,
396 data: DragData,
397 ) -> DragId {
398 let id = DragId::new(self.next_id);
399 self.next_id += 1;
400
401 let state = DragState::new(id, source_widget, position, data);
402 self.current_drag = Some(state);
403
404 id
405 }
406
407 pub fn move_drag(&mut self, position: Point) {
409 if let Some(state) = &mut self.current_drag {
410 state.current_position = position;
411
412 if state.phase == DragPhase::Started {
414 let distance = state.start_position.distance(&position);
415 if distance >= self.min_drag_distance {
416 state.phase = DragPhase::Dragging;
417 }
418 }
419
420 if state.phase == DragPhase::Dragging || state.phase == DragPhase::OverTarget {
422 let old_target = state.hover_target;
423 state.hover_target = None;
424 state.effect = DropEffect::None;
425
426 for target in self.targets.values() {
427 if target.contains_point(position) {
428 let effect = state
430 .allowed_effects
431 .iter()
432 .find(|e| target.accepts(&state.data, **e))
433 .copied()
434 .unwrap_or(DropEffect::None);
435
436 if effect != DropEffect::None {
437 state.hover_target = Some(target.widget_id);
438 state.effect = effect;
439 state.phase = DragPhase::OverTarget;
440 break;
441 }
442 }
443 }
444
445 if state.hover_target.is_none() && old_target.is_some() {
446 state.phase = DragPhase::Dragging;
447 }
448 }
449 }
450 }
451
452 pub fn drop(&mut self) -> Option<DropResult> {
454 let state = self.current_drag.take()?;
455
456 if let Some(target_id) = state.hover_target {
457 if state.effect != DropEffect::None {
458 return Some(DropResult {
459 success: true,
460 target: target_id,
461 effect: state.effect,
462 position: state.current_position,
463 });
464 }
465 }
466
467 Some(DropResult {
468 success: false,
469 target: state.source_widget,
470 effect: DropEffect::None,
471 position: state.current_position,
472 })
473 }
474
475 pub fn cancel(&mut self) {
477 if let Some(state) = &mut self.current_drag {
478 state.phase = DragPhase::Cancelled;
479 }
480 self.current_drag = None;
481 }
482
483 pub fn current(&self) -> Option<&DragState> {
485 self.current_drag.as_ref()
486 }
487
488 pub fn is_dragging(&self) -> bool {
490 self.current_drag.as_ref().is_some_and(DragState::is_active)
491 }
492
493 pub fn preview_position(&self) -> Option<Point> {
495 self.current_drag.as_ref().map(|s| {
496 Point::new(
497 s.current_position.x + self.preview_offset.x,
498 s.current_position.y + self.preview_offset.y,
499 )
500 })
501 }
502
503 pub fn target_count(&self) -> usize {
505 self.targets.len()
506 }
507
508 pub fn target_at(&self, position: Point) -> Option<&DropTarget> {
510 self.targets.values().find(|t| t.contains_point(position))
511 }
512
513 pub fn clear(&mut self) {
515 self.cancel();
516 self.targets.clear();
517 }
518}
519
520impl Default for DragDropManager {
521 fn default() -> Self {
522 Self::new()
523 }
524}
525
526impl std::fmt::Debug for DragDropManager {
527 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
528 f.debug_struct("DragDropManager")
529 .field("next_id", &self.next_id)
530 .field("is_dragging", &self.is_dragging())
531 .field("target_count", &self.targets.len())
532 .finish()
533 }
534}
535
536#[cfg(test)]
537#[allow(clippy::unwrap_used, clippy::disallowed_methods)]
538mod tests {
539 use super::*;
540
541 #[test]
543 fn test_drag_id() {
544 let id1 = DragId::new(1);
545 let id2 = DragId::new(1);
546 let id3 = DragId::new(2);
547
548 assert_eq!(id1, id2);
549 assert_ne!(id1, id3);
550 }
551
552 #[test]
554 fn test_drag_data_type() {
555 assert_eq!(DragDataType::Text, DragDataType::Text);
556 assert_ne!(DragDataType::Text, DragDataType::Html);
557
558 let custom = DragDataType::custom("my-type");
559 assert_eq!(custom, DragDataType::Custom("my-type".to_string()));
560 }
561
562 #[test]
564 fn test_drag_data_text() {
565 let data = DragData::text("Hello");
566 assert_eq!(data.data_type, DragDataType::Text);
567 assert_eq!(data.text, "Hello");
568 }
569
570 #[test]
571 fn test_drag_data_html() {
572 let data = DragData::html("<b>Bold</b>");
573 assert_eq!(data.data_type, DragDataType::Html);
574 assert!(data.has_format(&DragDataType::Html));
575 }
576
577 #[test]
578 fn test_drag_data_url() {
579 let data = DragData::url("https://example.com");
580 assert_eq!(data.data_type, DragDataType::Url);
581 assert_eq!(data.text, "https://example.com");
582 }
583
584 #[test]
585 fn test_drag_data_custom() {
586 let data = DragData::custom("widget-id", "123");
587 assert!(matches!(data.data_type, DragDataType::Custom(_)));
588 }
589
590 #[test]
591 fn test_drag_data_with_format() {
592 let data = DragData::text("Hello").with_format(DragDataType::Html, "<p>Hello</p>");
593
594 assert!(data.has_format(&DragDataType::Text));
595 assert!(data.has_format(&DragDataType::Html));
596 assert!(!data.has_format(&DragDataType::Url));
597
598 assert_eq!(data.get_format(&DragDataType::Text), Some("Hello"));
599 assert_eq!(data.get_format(&DragDataType::Html), Some("<p>Hello</p>"));
600 }
601
602 #[test]
603 fn test_drag_data_with_payload() {
604 let data = DragData::text("test").with_payload(42i32);
606 assert!(data.payload.is_some());
607 }
608
609 #[test]
611 fn test_drag_phase() {
612 assert_eq!(DragPhase::Started, DragPhase::Started);
613 assert_ne!(DragPhase::Started, DragPhase::Dropped);
614 }
615
616 #[test]
618 fn test_drop_effect_default() {
619 assert_eq!(DropEffect::default(), DropEffect::None);
620 }
621
622 #[test]
624 fn test_drag_state_new() {
625 let state = DragState::new(
626 DragId::new(1),
627 WidgetId::new(100),
628 Point::new(50.0, 50.0),
629 DragData::text("test"),
630 );
631
632 assert_eq!(state.id, DragId::new(1));
633 assert_eq!(state.source_widget, WidgetId::new(100));
634 assert_eq!(state.phase, DragPhase::Started);
635 assert_eq!(state.start_position, Point::new(50.0, 50.0));
636 assert!(state.is_active());
637 }
638
639 #[test]
640 fn test_drag_state_offset() {
641 let mut state = DragState::new(
642 DragId::new(1),
643 WidgetId::new(100),
644 Point::new(50.0, 50.0),
645 DragData::text("test"),
646 );
647
648 state.current_position = Point::new(100.0, 75.0);
649 let offset = state.offset();
650 assert_eq!(offset.x, 50.0);
651 assert_eq!(offset.y, 25.0);
652 }
653
654 #[test]
655 fn test_drag_state_is_active() {
656 let mut state = DragState::new(
657 DragId::new(1),
658 WidgetId::new(100),
659 Point::ORIGIN,
660 DragData::text("test"),
661 );
662
663 assert!(state.is_active());
664
665 state.phase = DragPhase::Dragging;
666 assert!(state.is_active());
667
668 state.phase = DragPhase::OverTarget;
669 assert!(state.is_active());
670
671 state.phase = DragPhase::Dropped;
672 assert!(!state.is_active());
673
674 state.phase = DragPhase::Cancelled;
675 assert!(!state.is_active());
676 }
677
678 #[test]
680 fn test_drop_target_new() {
681 let target = DropTarget::new(WidgetId::new(1), Rect::new(0.0, 0.0, 100.0, 100.0));
682
683 assert_eq!(target.widget_id, WidgetId::new(1));
684 assert!(target.enabled);
685 assert!(target.accepted_types.is_empty());
686 }
687
688 #[test]
689 fn test_drop_target_accept_types() {
690 let target = DropTarget::new(WidgetId::new(1), Rect::new(0.0, 0.0, 100.0, 100.0))
691 .accept_types(vec![DragDataType::Text, DragDataType::Html]);
692
693 assert_eq!(target.accepted_types.len(), 2);
694 }
695
696 #[test]
697 fn test_drop_target_accepts() {
698 let target = DropTarget::new(WidgetId::new(1), Rect::new(0.0, 0.0, 100.0, 100.0))
699 .accept_types(vec![DragDataType::Text])
700 .accept_effects(vec![DropEffect::Copy]);
701
702 let text_data = DragData::text("hello");
703 assert!(target.accepts(&text_data, DropEffect::Copy));
704 assert!(!target.accepts(&text_data, DropEffect::Move));
705
706 let html_data = DragData::html("<b>bold</b>");
707 assert!(!target.accepts(&html_data, DropEffect::Copy));
708 }
709
710 #[test]
711 fn test_drop_target_accepts_all_types() {
712 let target = DropTarget::new(WidgetId::new(1), Rect::new(0.0, 0.0, 100.0, 100.0));
714
715 assert!(target.accepts(&DragData::text("test"), DropEffect::Copy));
716 assert!(target.accepts(&DragData::html("<b>test</b>"), DropEffect::Move));
717 }
718
719 #[test]
720 fn test_drop_target_disabled() {
721 let mut target = DropTarget::new(WidgetId::new(1), Rect::new(0.0, 0.0, 100.0, 100.0));
722 target.enabled = false;
723
724 assert!(!target.accepts(&DragData::text("test"), DropEffect::Copy));
725 assert!(!target.contains_point(Point::new(50.0, 50.0)));
726 }
727
728 #[test]
729 fn test_drop_target_contains_point() {
730 let target = DropTarget::new(WidgetId::new(1), Rect::new(10.0, 10.0, 100.0, 100.0));
731
732 assert!(target.contains_point(Point::new(50.0, 50.0)));
733 assert!(target.contains_point(Point::new(10.0, 10.0)));
734 assert!(!target.contains_point(Point::new(5.0, 50.0)));
735 assert!(!target.contains_point(Point::new(120.0, 50.0)));
736 }
737
738 #[test]
740 fn test_manager_new() {
741 let manager = DragDropManager::new();
742 assert!(!manager.is_dragging());
743 assert_eq!(manager.target_count(), 0);
744 }
745
746 #[test]
747 fn test_manager_register_target() {
748 let mut manager = DragDropManager::new();
749
750 manager.register_target(DropTarget::new(
751 WidgetId::new(1),
752 Rect::new(0.0, 0.0, 100.0, 100.0),
753 ));
754
755 assert_eq!(manager.target_count(), 1);
756 }
757
758 #[test]
759 fn test_manager_unregister_target() {
760 let mut manager = DragDropManager::new();
761
762 manager.register_target(DropTarget::new(
763 WidgetId::new(1),
764 Rect::new(0.0, 0.0, 100.0, 100.0),
765 ));
766 manager.unregister_target(WidgetId::new(1));
767
768 assert_eq!(manager.target_count(), 0);
769 }
770
771 #[test]
772 fn test_manager_start_drag() {
773 let mut manager = DragDropManager::new();
774
775 let id = manager.start_drag(
776 WidgetId::new(1),
777 Point::new(50.0, 50.0),
778 DragData::text("hello"),
779 );
780
781 assert!(manager.is_dragging());
782 assert_eq!(manager.current().unwrap().id, id);
783 }
784
785 #[test]
786 fn test_manager_move_drag() {
787 let mut manager = DragDropManager::new();
788 manager.set_min_drag_distance(5.0);
789
790 manager.start_drag(
791 WidgetId::new(1),
792 Point::new(50.0, 50.0),
793 DragData::text("hello"),
794 );
795
796 manager.move_drag(Point::new(52.0, 52.0));
798 assert_eq!(manager.current().unwrap().phase, DragPhase::Started);
799
800 manager.move_drag(Point::new(60.0, 60.0));
802 assert_eq!(manager.current().unwrap().phase, DragPhase::Dragging);
803 }
804
805 #[test]
806 fn test_manager_move_over_target() {
807 let mut manager = DragDropManager::new();
808 manager.set_min_drag_distance(0.0);
809
810 manager.register_target(DropTarget::new(
811 WidgetId::new(10),
812 Rect::new(100.0, 100.0, 100.0, 100.0),
813 ));
814
815 manager.start_drag(
816 WidgetId::new(1),
817 Point::new(50.0, 50.0),
818 DragData::text("hello"),
819 );
820
821 manager.move_drag(Point::new(150.0, 150.0));
823 let state = manager.current().unwrap();
824 assert_eq!(state.phase, DragPhase::OverTarget);
825 assert_eq!(state.hover_target, Some(WidgetId::new(10)));
826 }
827
828 #[test]
829 fn test_manager_drop_success() {
830 let mut manager = DragDropManager::new();
831 manager.set_min_drag_distance(0.0);
832
833 manager.register_target(DropTarget::new(
834 WidgetId::new(10),
835 Rect::new(100.0, 100.0, 100.0, 100.0),
836 ));
837
838 manager.start_drag(
839 WidgetId::new(1),
840 Point::new(50.0, 50.0),
841 DragData::text("hello"),
842 );
843
844 manager.move_drag(Point::new(150.0, 150.0));
845 let result = manager.drop().unwrap();
846
847 assert!(result.success);
848 assert_eq!(result.target, WidgetId::new(10));
849 assert!(!manager.is_dragging());
850 }
851
852 #[test]
853 fn test_manager_drop_failure() {
854 let mut manager = DragDropManager::new();
855 manager.set_min_drag_distance(0.0);
856
857 manager.start_drag(
858 WidgetId::new(1),
859 Point::new(50.0, 50.0),
860 DragData::text("hello"),
861 );
862
863 manager.move_drag(Point::new(60.0, 60.0));
864 let result = manager.drop().unwrap();
865
866 assert!(!result.success);
867 assert_eq!(result.effect, DropEffect::None);
868 }
869
870 #[test]
871 fn test_manager_cancel() {
872 let mut manager = DragDropManager::new();
873
874 manager.start_drag(
875 WidgetId::new(1),
876 Point::new(50.0, 50.0),
877 DragData::text("hello"),
878 );
879
880 manager.cancel();
881 assert!(!manager.is_dragging());
882 assert!(manager.current().is_none());
883 }
884
885 #[test]
886 fn test_manager_preview_position() {
887 let mut manager = DragDropManager::new();
888 manager.set_preview_offset(Point::new(-10.0, -10.0));
889
890 manager.start_drag(
891 WidgetId::new(1),
892 Point::new(100.0, 100.0),
893 DragData::text("hello"),
894 );
895
896 let preview_pos = manager.preview_position().unwrap();
897 assert_eq!(preview_pos, Point::new(90.0, 90.0));
898 }
899
900 #[test]
901 fn test_manager_target_at() {
902 let mut manager = DragDropManager::new();
903
904 manager.register_target(DropTarget::new(
905 WidgetId::new(1),
906 Rect::new(0.0, 0.0, 100.0, 100.0),
907 ));
908 manager.register_target(DropTarget::new(
909 WidgetId::new(2),
910 Rect::new(200.0, 200.0, 100.0, 100.0),
911 ));
912
913 assert!(manager.target_at(Point::new(50.0, 50.0)).is_some());
914 assert!(manager.target_at(Point::new(150.0, 150.0)).is_none());
915 assert!(manager.target_at(Point::new(250.0, 250.0)).is_some());
916 }
917
918 #[test]
919 fn test_manager_clear() {
920 let mut manager = DragDropManager::new();
921
922 manager.register_target(DropTarget::new(
923 WidgetId::new(1),
924 Rect::new(0.0, 0.0, 100.0, 100.0),
925 ));
926
927 manager.start_drag(WidgetId::new(2), Point::ORIGIN, DragData::text("test"));
928
929 manager.clear();
930
931 assert!(!manager.is_dragging());
932 assert_eq!(manager.target_count(), 0);
933 }
934
935 #[test]
936 fn test_manager_update_target_bounds() {
937 let mut manager = DragDropManager::new();
938
939 manager.register_target(DropTarget::new(
940 WidgetId::new(1),
941 Rect::new(0.0, 0.0, 100.0, 100.0),
942 ));
943
944 manager.update_target_bounds(WidgetId::new(1), Rect::new(50.0, 50.0, 200.0, 200.0));
945
946 let target = manager.target_at(Point::new(100.0, 100.0)).unwrap();
947 assert_eq!(target.bounds.x, 50.0);
948 assert_eq!(target.bounds.width, 200.0);
949 }
950}