1use blitz_dom::{BaseDocument, Node};
2use blitz_traits::events::{
3 BlitzKeyEvent, BlitzPointerEvent, BlitzPointerId, BlitzScrollEvent, BlitzWheelDelta,
4 BlitzWheelEvent, MouseEventButton,
5};
6use dioxus_html::{
7 AnimationData, CancelData, ClipboardData, CompositionData, DragData, FocusData, FormData,
8 FormValue, HasFileData, HasFocusData, HasFormData, HasKeyboardData, HasMouseData,
9 HasPointerData, HasScrollData, HasTouchData, HasTouchPointData, HasWheelData,
10 HtmlEventConverter, ImageData, KeyboardData, MediaData, MountedData, MountedError,
11 MountedResult, MouseData, PlatformEventData, PointerData, RenderedElementBacking, ResizeData,
12 ScrollBehavior, ScrollData, ScrollToOptions, SelectionData, ToggleData, TouchData, TouchPoint,
13 TransitionData, VisibleData, WheelData,
14 geometry::{
15 ClientPoint, ElementPoint, PagePoint, PixelsRect, PixelsSize, PixelsVector2D, ScreenPoint,
16 WheelDelta,
17 euclid::{Point2D, Size2D, Vector3D},
18 },
19 input_data::{MouseButton, MouseButtonSet},
20 point_interaction::{
21 InteractionElementOffset, InteractionLocation, ModifiersInteraction, PointerInteraction,
22 },
23};
24use keyboard_types::{Code, Key, Location, Modifiers};
25use std::{
26 any::Any,
27 cell::{Ref, RefCell, RefMut},
28 fmt::Display,
29 future::Future,
30 pin::Pin,
31 rc::Rc,
32};
33
34use crate::NodeId;
35
36pub struct NativeConverter {}
37
38impl HtmlEventConverter for NativeConverter {
39 fn convert_cancel_data(&self, _event: &PlatformEventData) -> CancelData {
40 unimplemented!("todo: convert_cancel_data in dioxus-native. requires support in blitz")
41 }
42
43 fn convert_form_data(&self, event: &PlatformEventData) -> FormData {
44 event.downcast::<NativeFormData>().unwrap().clone().into()
45 }
46
47 fn convert_mouse_data(&self, event: &PlatformEventData) -> MouseData {
48 event
49 .downcast::<NativePointerData>()
50 .unwrap()
51 .clone()
52 .into()
53 }
54
55 fn convert_keyboard_data(&self, event: &PlatformEventData) -> KeyboardData {
56 event
57 .downcast::<BlitzKeyboardData>()
58 .unwrap()
59 .clone()
60 .into()
61 }
62
63 fn convert_focus_data(&self, _event: &PlatformEventData) -> FocusData {
64 NativeFocusData {}.into()
65 }
66
67 fn convert_animation_data(&self, _event: &PlatformEventData) -> AnimationData {
68 unimplemented!("todo: convert_animation_data in dioxus-native. requires support in blitz")
69 }
70
71 fn convert_clipboard_data(&self, _event: &PlatformEventData) -> ClipboardData {
72 unimplemented!("todo: convert_clipboard_data in dioxus-native. requires support in blitz")
73 }
74
75 fn convert_composition_data(&self, _event: &PlatformEventData) -> CompositionData {
76 unimplemented!("todo: convert_composition_data in dioxus-native. requires support in blitz")
77 }
78
79 fn convert_drag_data(&self, _event: &PlatformEventData) -> DragData {
80 unimplemented!("todo: convert_drag_data in dioxus-native. requires support in blitz")
81 }
82
83 fn convert_image_data(&self, _event: &PlatformEventData) -> ImageData {
84 unimplemented!("todo: convert_image_data in dioxus-native. requires support in blitz")
85 }
86
87 fn convert_media_data(&self, _event: &PlatformEventData) -> MediaData {
88 unimplemented!("todo: convert_media_data in dioxus-native. requires support in blitz")
89 }
90
91 fn convert_mounted_data(&self, event: &PlatformEventData) -> MountedData {
92 event.downcast::<NodeHandle>().unwrap().clone().into()
93 }
94
95 fn convert_pointer_data(&self, event: &PlatformEventData) -> PointerData {
96 event
97 .downcast::<NativePointerData>()
98 .unwrap()
99 .clone()
100 .into()
101 }
102
103 fn convert_scroll_data(&self, event: &PlatformEventData) -> ScrollData {
104 event.downcast::<NativeScrollData>().unwrap().clone().into()
105 }
106
107 fn convert_selection_data(&self, _event: &PlatformEventData) -> SelectionData {
108 unimplemented!("todo: convert_selection_data in dioxus-native. requires support in blitz")
109 }
110
111 fn convert_toggle_data(&self, _event: &PlatformEventData) -> ToggleData {
112 unimplemented!("todo: convert_toggle_data in dioxus-native. requires support in blitz")
113 }
114
115 fn convert_touch_data(&self, event: &PlatformEventData) -> TouchData {
116 event.downcast::<NativeTouchData>().unwrap().clone().into()
117 }
118
119 fn convert_transition_data(&self, _event: &PlatformEventData) -> TransitionData {
120 unimplemented!("todo: convert_transition_data in dioxus-native. requires support in blitz")
121 }
122
123 fn convert_wheel_data(&self, event: &PlatformEventData) -> WheelData {
124 event.downcast::<NativeWheelData>().unwrap().clone().into()
125 }
126
127 fn convert_resize_data(&self, _event: &PlatformEventData) -> ResizeData {
128 unimplemented!("todo: convert_resize_data in dioxus-native. requires support in blitz")
129 }
130
131 fn convert_visible_data(&self, _event: &PlatformEventData) -> VisibleData {
132 unimplemented!("todo: convert_visible_data in dioxus-native. requires support in blitz")
133 }
134}
135
136pub(crate) enum DocumentCommand {
137 SetFocus { node_id: NodeId, focus: bool },
138}
139
140pub(crate) type DocumentCommandQueue = Rc<RefCell<Vec<DocumentCommand>>>;
141
142fn apply_focus(doc: &mut BaseDocument, node_id: NodeId, focus: bool) {
143 if focus {
144 doc.set_focus_to(node_id);
145 } else if doc.get_focussed_node_id() == Some(node_id) {
146 doc.clear_focus();
147 }
148}
149
150fn queue_focus(queue: &DocumentCommandQueue, node_id: NodeId, focus: bool) {
151 queue
152 .borrow_mut()
153 .push(DocumentCommand::SetFocus { node_id, focus });
154}
155
156pub(crate) fn flush_document_commands(
162 doc: &Rc<RefCell<BaseDocument>>,
163 queue: &DocumentCommandQueue,
164) {
165 let queued = std::mem::take(&mut *queue.borrow_mut());
166 for command in queued {
167 let DocumentCommand::SetFocus { node_id, focus } = command;
168 let applied = match doc.try_borrow_mut() {
172 Ok(mut doc_ref) => {
173 apply_focus(&mut doc_ref, node_id, focus);
174 true
175 }
176 Err(_) => false,
177 };
178 if !applied {
179 queue_focus(queue, node_id, focus);
180 }
181 }
182}
183
184#[derive(Clone)]
185pub struct NodeHandle {
186 pub(crate) doc: Rc<RefCell<BaseDocument>>,
187 pub(crate) command_queue: DocumentCommandQueue,
188 pub(crate) node_id: NodeId,
189}
190
191impl NodeHandle {
192 pub fn node_id(&self) -> NodeId {
193 self.node_id
194 }
195
196 pub fn doc(&self) -> Ref<'_, BaseDocument> {
197 self.doc.borrow()
198 }
199
200 pub fn try_doc(&self) -> Option<Ref<'_, BaseDocument>> {
203 self.doc.try_borrow().ok()
204 }
205
206 pub fn doc_mut(&self) -> RefMut<'_, BaseDocument> {
207 self.doc.borrow_mut()
208 }
209
210 pub fn try_doc_mut(&self) -> Option<RefMut<'_, BaseDocument>> {
216 self.doc.try_borrow_mut().ok()
217 }
218
219 pub fn node(&self) -> Ref<'_, Node> {
220 Ref::map(self.doc.borrow(), |doc| {
221 doc.get_node(self.node_id)
222 .expect("Node does not exist in the Document")
223 })
224 }
225
226 pub fn node_mut(&self) -> RefMut<'_, Node> {
227 RefMut::map(self.doc.borrow_mut(), |doc| {
228 doc.get_node_mut(self.node_id)
229 .expect("Node does not exist in the Document")
230 })
231 }
232
233 fn node_not_exist_err<T>(&self) -> Pin<Box<dyn Future<Output = MountedResult<T>>>> {
234 let node_id = self.node_id;
235 let err = MountedError::OperationFailed(Box::new(NodeNotExistErr(node_id)));
236 Box::pin(async move { Err(err) })
237 }
238}
239
240#[derive(Debug)]
241struct NodeNotExistErr(NodeId);
242impl Display for NodeNotExistErr {
243 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
244 write!(f, "The node {} does not exist", self.0)
245 }
246}
247impl std::error::Error for NodeNotExistErr {}
248
249impl RenderedElementBacking for NodeHandle {
250 fn as_any(&self) -> &dyn std::any::Any {
251 self
252 }
253
254 fn get_scroll_offset(&self) -> Pin<Box<dyn Future<Output = MountedResult<PixelsVector2D>>>> {
255 let scroll_offset = *self.node().scroll_offset();
256 Box::pin(async move { Ok(PixelsVector2D::new(scroll_offset.x, scroll_offset.y)) })
257 }
258
259 fn get_scroll_size(&self) -> Pin<Box<dyn Future<Output = MountedResult<PixelsSize>>>> {
260 let node = self.node();
261 let scroll_width = node.final_layout().scroll_width() as f64;
262 let scroll_height = node.final_layout().scroll_height() as f64;
263 Box::pin(async move { Ok(PixelsSize::new(scroll_width, scroll_height)) })
264 }
265
266 fn get_client_rect(&self) -> Pin<Box<dyn Future<Output = MountedResult<PixelsRect>>>> {
267 let Some(bounding_rect) = self.doc_mut().get_client_bounding_rect(self.node_id) else {
268 return self.node_not_exist_err();
269 };
270 let pixels_rect = PixelsRect::new(
271 Point2D::new(bounding_rect.x, bounding_rect.y),
272 Size2D::new(bounding_rect.width, bounding_rect.height),
273 );
274 Box::pin(async move { Ok(pixels_rect) })
275 }
276
277 fn scroll_to(
278 &self,
279 _options: ScrollToOptions,
280 ) -> Pin<Box<dyn Future<Output = MountedResult<()>>>> {
281 Box::pin(async { Err(MountedError::NotSupported) })
282 }
283
284 fn scroll(
285 &self,
286 _coordinates: PixelsVector2D,
287 _behavior: ScrollBehavior,
288 ) -> Pin<Box<dyn Future<Output = MountedResult<()>>>> {
289 Box::pin(async { Err(MountedError::NotSupported) })
290 }
291
292 fn set_focus(&self, focus: bool) -> Pin<Box<dyn Future<Output = MountedResult<()>>>> {
293 match self.doc.try_borrow_mut() {
308 Ok(mut doc) => apply_focus(&mut doc, self.node_id, focus),
309 Err(_) => queue_focus(&self.command_queue, self.node_id, focus),
310 }
311
312 Box::pin(async { Ok(()) })
313 }
314}
315
316#[derive(Clone, Debug)]
317pub struct NativeFormData {
318 pub value: String,
319 pub values: Vec<(String, FormValue)>,
320}
321
322impl HasFormData for NativeFormData {
323 fn as_any(&self) -> &dyn Any {
324 self as &dyn Any
325 }
326
327 fn value(&self) -> String {
328 self.value.clone()
329 }
330
331 fn values(&self) -> Vec<(String, FormValue)> {
332 self.values.clone()
333 }
334 fn valid(&self) -> bool {
335 true
337 }
338}
339
340impl HasFileData for NativeFormData {
341 fn files(&self) -> Vec<dioxus_html::FileData> {
342 vec![]
343 }
344}
345
346#[derive(Clone, Debug)]
347pub(crate) struct BlitzKeyboardData(pub(crate) BlitzKeyEvent);
348
349impl ModifiersInteraction for BlitzKeyboardData {
350 fn modifiers(&self) -> Modifiers {
351 self.0.modifiers
352 }
353}
354
355impl HasKeyboardData for BlitzKeyboardData {
356 fn key(&self) -> Key {
357 self.0.key.clone()
358 }
359
360 fn code(&self) -> Code {
361 self.0.code
362 }
363
364 fn location(&self) -> Location {
365 self.0.location
366 }
367
368 fn is_auto_repeating(&self) -> bool {
369 self.0.is_auto_repeating
370 }
371
372 fn is_composing(&self) -> bool {
373 self.0.is_composing
374 }
375
376 fn as_any(&self) -> &dyn Any {
377 self as &dyn Any
378 }
379}
380
381#[derive(Clone)]
382pub struct NativePointerData(pub(crate) BlitzPointerEvent);
383
384impl InteractionLocation for NativePointerData {
385 fn client_coordinates(&self) -> ClientPoint {
386 ClientPoint::new(self.0.client_x() as f64, self.0.client_y() as f64)
387 }
388
389 fn screen_coordinates(&self) -> ScreenPoint {
390 ScreenPoint::new(self.0.screen_x() as f64, self.0.screen_y() as f64)
391 }
392
393 fn page_coordinates(&self) -> PagePoint {
394 PagePoint::new(self.0.page_x() as f64, self.0.page_y() as f64)
395 }
396}
397
398impl InteractionElementOffset for NativePointerData {
399 fn element_coordinates(&self) -> ElementPoint {
400 ElementPoint::new(self.0.element_x() as f64, self.0.element_y() as f64)
401 }
402}
403
404impl ModifiersInteraction for NativePointerData {
405 fn modifiers(&self) -> Modifiers {
406 self.0.mods
407 }
408}
409
410impl PointerInteraction for NativePointerData {
411 fn trigger_button(&self) -> Option<MouseButton> {
412 Some(match self.0.button {
413 MouseEventButton::Main => MouseButton::Primary,
414 MouseEventButton::Auxiliary => MouseButton::Auxiliary,
415 MouseEventButton::Secondary => MouseButton::Secondary,
416 MouseEventButton::Fourth => MouseButton::Fourth,
417 MouseEventButton::Fifth => MouseButton::Fifth,
418 })
419 }
420
421 fn held_buttons(&self) -> MouseButtonSet {
422 dioxus_html::input_data::decode_mouse_button_set(self.0.buttons.bits() as u16)
423 }
424}
425impl HasMouseData for NativePointerData {
426 fn as_any(&self) -> &dyn Any {
427 self as &dyn Any
428 }
429}
430
431impl HasPointerData for NativePointerData {
432 fn as_any(&self) -> &dyn Any {
433 self as &dyn Any
434 }
435
436 fn is_primary(&self) -> bool {
437 self.0.is_primary
438 }
439
440 fn pointer_id(&self) -> i32 {
441 match self.0.id {
442 BlitzPointerId::Mouse => 0,
443 BlitzPointerId::Pen => 0,
444 BlitzPointerId::Finger(id) => id as i32,
445 }
446 }
447
448 fn pointer_type(&self) -> String {
449 match self.0.id {
450 BlitzPointerId::Mouse => String::from("mouse"),
451 BlitzPointerId::Pen => String::from("pen"),
452 BlitzPointerId::Finger(_) => String::from("touch"),
453 }
454 }
455
456 fn pressure(&self) -> f32 {
457 self.0.details.pressure as f32
458 }
459 fn tangential_pressure(&self) -> f32 {
460 self.0.details.tangential_pressure
461 }
462 fn tilt_x(&self) -> i32 {
463 self.0.details.tilt_x as i32
464 }
465 fn tilt_y(&self) -> i32 {
466 self.0.details.tilt_y as i32
467 }
468 fn twist(&self) -> i32 {
469 self.0.details.twist as i32
470 }
471
472 fn width(&self) -> f64 {
474 1.0
475 }
476 fn height(&self) -> f64 {
477 1.0
478 }
479}
480
481#[derive(Clone)]
488pub struct NativeTouchData(pub(crate) BlitzPointerEvent);
489
490impl ModifiersInteraction for NativeTouchData {
491 fn modifiers(&self) -> Modifiers {
492 self.0.mods
493 }
494}
495
496impl HasTouchData for NativeTouchData {
497 fn touches(&self) -> Vec<TouchPoint> {
498 self.0
500 .active_pointers
501 .borrow()
502 .iter()
503 .map(|event| TouchPoint::new(NativeTouchPointData(event.clone())))
504 .collect()
505 }
506
507 fn touches_changed(&self) -> Vec<TouchPoint> {
508 vec![TouchPoint::new(NativeTouchPointData(self.0.clone()))]
510 }
511
512 fn target_touches(&self) -> Vec<TouchPoint> {
513 self.touches()
516 }
517
518 fn as_any(&self) -> &dyn Any {
519 self as &dyn Any
520 }
521}
522
523#[derive(Clone)]
524pub struct NativeTouchPointData(BlitzPointerEvent);
525
526impl InteractionLocation for NativeTouchPointData {
527 fn client_coordinates(&self) -> ClientPoint {
528 ClientPoint::new(self.0.client_x() as f64, self.0.client_y() as f64)
529 }
530
531 fn screen_coordinates(&self) -> ScreenPoint {
532 ScreenPoint::new(self.0.screen_x() as f64, self.0.screen_y() as f64)
533 }
534
535 fn page_coordinates(&self) -> PagePoint {
536 PagePoint::new(self.0.page_x() as f64, self.0.page_y() as f64)
537 }
538}
539
540impl HasTouchPointData for NativeTouchPointData {
541 fn identifier(&self) -> i32 {
542 match self.0.id {
543 BlitzPointerId::Finger(id) => id as i32,
544 BlitzPointerId::Mouse | BlitzPointerId::Pen => 0,
545 }
546 }
547
548 fn force(&self) -> f64 {
549 self.0.details.pressure
550 }
551
552 fn radius(&self) -> ScreenPoint {
553 ScreenPoint::new(1.0, 1.0)
555 }
556
557 fn rotation(&self) -> f64 {
558 0.0
560 }
561
562 fn as_any(&self) -> &dyn Any {
563 self as &dyn Any
564 }
565}
566
567#[derive(Clone)]
568pub struct NativeFocusData;
569impl HasFocusData for NativeFocusData {
570 fn as_any(&self) -> &dyn Any {
571 self as &dyn Any
572 }
573}
574
575#[derive(Clone)]
576pub struct NativeScrollData(pub(crate) BlitzScrollEvent);
577impl HasScrollData for NativeScrollData {
578 fn as_any(&self) -> &dyn Any {
579 self as &dyn Any
580 }
581
582 fn scroll_top(&self) -> f64 {
583 self.0.scroll_top
584 }
585
586 fn scroll_left(&self) -> f64 {
587 self.0.scroll_left
588 }
589
590 fn scroll_width(&self) -> i32 {
591 self.0.scroll_width
592 }
593
594 fn scroll_height(&self) -> i32 {
595 self.0.scroll_height
596 }
597
598 fn client_width(&self) -> i32 {
599 self.0.client_width
600 }
601
602 fn client_height(&self) -> i32 {
603 self.0.client_height
604 }
605}
606
607#[derive(Clone)]
608pub struct NativeWheelData(pub(crate) BlitzWheelEvent);
609impl HasWheelData for NativeWheelData {
610 fn as_any(&self) -> &dyn Any {
611 self as &dyn Any
612 }
613
614 fn delta(&self) -> WheelDelta {
615 match self.0.delta {
616 BlitzWheelDelta::Lines(x, y) => {
617 dioxus_html::geometry::WheelDelta::Lines(Vector3D::new(x, y, 0.0))
618 }
619 BlitzWheelDelta::Pixels(x, y) => {
620 dioxus_html::geometry::WheelDelta::Pixels(Vector3D::new(x, y, 0.0))
621 }
622 }
623 }
624}
625
626impl HasMouseData for NativeWheelData {
627 fn as_any(&self) -> &dyn Any {
628 self as &dyn Any
629 }
630}
631
632impl PointerInteraction for NativeWheelData {
633 fn trigger_button(&self) -> Option<MouseButton> {
634 None
635 }
636
637 fn held_buttons(&self) -> MouseButtonSet {
638 dioxus_html::input_data::decode_mouse_button_set(self.0.buttons.bits() as u16)
639 }
640}
641
642impl ModifiersInteraction for NativeWheelData {
643 fn modifiers(&self) -> Modifiers {
644 self.0.mods
645 }
646}
647
648impl InteractionElementOffset for NativeWheelData {
649 fn element_coordinates(&self) -> ElementPoint {
650 ElementPoint::new(self.0.element_x() as f64, self.0.element_y() as f64)
651 }
652}
653
654impl InteractionLocation for NativeWheelData {
655 fn client_coordinates(&self) -> ClientPoint {
656 ClientPoint::new(self.0.client_x() as f64, self.0.client_y() as f64)
657 }
658
659 fn screen_coordinates(&self) -> ScreenPoint {
660 ScreenPoint::new(self.0.screen_x() as f64, self.0.screen_y() as f64)
661 }
662
663 fn page_coordinates(&self) -> PagePoint {
664 PagePoint::new(self.0.page_x() as f64, self.0.page_y() as f64)
665 }
666}
667
668pub fn synthetic_click_event(node: &Node, modifiers: Modifiers) -> Box<dyn Any> {
669 Box::new(NativePointerData(
670 node.synthetic_click_event_data(modifiers),
671 ))
672}
673
674#[cfg(test)]
675mod tests {
676 use super::*;
677 use blitz_traits::events::{
678 BlitzPointerId, MouseEventButton, MouseEventButtons, Point, PointerCoords, PointerDetails,
679 };
680
681 fn finger_event(id: u64, x: f32, y: f32) -> BlitzPointerEvent {
682 BlitzPointerEvent {
683 id: BlitzPointerId::Finger(id),
684 is_primary: id == 0,
685 coords: PointerCoords {
686 page_x: x,
687 page_y: y,
688 screen_x: x,
689 screen_y: y,
690 client_x: x,
691 client_y: y,
692 },
693 button: MouseEventButton::Main,
694 buttons: MouseEventButtons::from(MouseEventButton::Main),
695 mods: Default::default(),
696 details: PointerDetails::default(),
697 element: Point::default(),
698 active_pointers: Default::default(),
699 }
700 }
701
702 #[test]
703 fn touches_reports_all_active_pointers() {
704 let f0 = finger_event(0, 10.0, 20.0);
705 let f1 = finger_event(1, 30.0, 40.0);
706
707 let trigger = f1.clone();
710 {
711 let mut list = trigger.active_pointers.borrow_mut();
712 list.push(f0.clone());
713 list.push(f1.clone());
714 }
715
716 let data = NativeTouchData(trigger);
717
718 let touches = data.touches();
720 assert_eq!(touches.len(), 2);
721 let coords: Vec<(f64, f64)> = touches
722 .iter()
723 .map(|t| {
724 let c = t.client_coordinates();
725 (c.x, c.y)
726 })
727 .collect();
728 assert!(coords.contains(&(10.0, 20.0)));
729 assert!(coords.contains(&(30.0, 40.0)));
730
731 assert_eq!(data.touches_changed().len(), 1);
733 let changed = data.touches_changed();
734 let changed_coords = changed[0].client_coordinates();
735 assert_eq!((changed_coords.x, changed_coords.y), (30.0, 40.0));
736 }
737
738 #[test]
749 fn focus_requested_while_the_document_is_borrowed_is_applied_later() {
750 use blitz_dom::DocumentConfig;
751
752 let doc = Rc::new(RefCell::new(BaseDocument::new(DocumentConfig::default())));
753 let root_id = doc.borrow().root_node().id;
754 let handle = NodeHandle {
755 doc: Rc::clone(&doc),
756 command_queue: Rc::new(RefCell::new(Vec::new())),
757 node_id: root_id,
758 };
759
760 {
761 let _held = doc.borrow_mut();
762 let _ = handle.set_focus(true);
764 }
765
766 assert_eq!(
767 doc.borrow().get_focussed_node_id(),
768 None,
769 "the request cannot have been applied while the document was borrowed"
770 );
771
772 flush_document_commands(&doc, &handle.command_queue);
773
774 assert_eq!(
775 doc.borrow().get_focussed_node_id(),
776 Some(root_id),
777 "the queued request should be applied once the borrow is released"
778 );
779 }
780
781 #[test]
784 fn focus_applies_immediately_when_the_document_is_free() {
785 use blitz_dom::DocumentConfig;
786
787 let doc = Rc::new(RefCell::new(BaseDocument::new(DocumentConfig::default())));
788 let root_id = doc.borrow().root_node().id;
789 let handle = NodeHandle {
790 doc: Rc::clone(&doc),
791 command_queue: Rc::new(RefCell::new(Vec::new())),
792 node_id: root_id,
793 };
794
795 let _ = handle.set_focus(true);
796
797 assert_eq!(doc.borrow().get_focussed_node_id(), Some(root_id));
798 }
799
800 #[test]
801 fn flushing_one_document_does_not_apply_another_documents_focus() {
802 use blitz_dom::DocumentConfig;
803
804 let first = Rc::new(RefCell::new(BaseDocument::new(DocumentConfig::default())));
805 let second = Rc::new(RefCell::new(BaseDocument::new(DocumentConfig::default())));
806 let first_id = first.borrow().root_node().id;
807 let second_id = second.borrow().root_node().id;
808 let first_queue = Rc::new(RefCell::new(Vec::new()));
809 let second_queue = Rc::new(RefCell::new(Vec::new()));
810 let first_handle = NodeHandle {
811 doc: Rc::clone(&first),
812 command_queue: Rc::clone(&first_queue),
813 node_id: first_id,
814 };
815 let second_handle = NodeHandle {
816 doc: Rc::clone(&second),
817 command_queue: Rc::clone(&second_queue),
818 node_id: second_id,
819 };
820
821 {
822 let _first_borrow = first.borrow_mut();
823 let _second_borrow = second.borrow_mut();
824 let _ = first_handle.set_focus(true);
825 let _ = second_handle.set_focus(true);
826 }
827
828 flush_document_commands(&first, &first_queue);
831
832 assert_eq!(first.borrow().get_focussed_node_id(), Some(first_id));
833 assert_eq!(
834 second.borrow().get_focussed_node_id(),
835 None,
836 "polling one document must not execute another document's commands",
837 );
838 }
839
840 #[test]
841 fn touches_is_empty_when_no_pointers_are_active() {
842 let data = NativeTouchData(finger_event(0, 1.0, 2.0));
846 assert!(data.touches().is_empty());
847 assert_eq!(data.touches_changed().len(), 1);
848 }
849}