1use crate::NodeId;
3use crate::events::{
4 BlitzKeyboardData, NativeConverter, NativeFocusData, NativeFormData, NativePointerData,
5 NativeScrollData, NativeTouchData, NativeWheelData, NodeHandle,
6};
7use crate::mutation_writer::{DioxusState, MutationWriter};
8use crate::qual_name;
9use blitz_dom::{
10 Attribute, BaseDocument, DEFAULT_CSS, DocGuard, DocGuardMut, Document, DocumentConfig,
11 EventDriver, EventHandler, Node,
12};
13use blitz_traits::events::{DomEvent, DomEventData, EventState, UiEvent};
14use dioxus_core::{ElementId, Event, VirtualDom};
15use dioxus_html::{PlatformEventData, set_event_converter};
16use futures_util::task::noop_waker;
17use std::cell::RefCell;
18use std::future::Future;
19use std::mem;
20use std::pin::pin;
21use std::sync::LazyLock;
22use std::task::{Context as TaskContext, Waker};
23use std::{any::Any, rc::Rc};
24
25fn wrap_event_data<T: Any>(value: T) -> Rc<dyn Any> {
26 Rc::new(PlatformEventData::new(Box::new(value)))
27}
28
29fn get_dioxus_id(node: &Node) -> Option<ElementId> {
31 node.element_data()?
32 .attrs
33 .iter()
34 .find(|attr| *attr.name.local == *"data-dioxus-id")
35 .and_then(|attr| attr.value.parse::<usize>().ok())
36 .map(ElementId)
37}
38
39pub struct DioxusDocument {
68 pub inner: Rc<RefCell<BaseDocument>>,
69 pub vdom: VirtualDom,
70 pub vdom_state: DioxusState,
71
72 #[allow(unused)]
73 pub(crate) html_element_id: NodeId,
74 #[allow(unused)]
75 pub(crate) head_element_id: NodeId,
76 #[allow(unused)]
77 pub(crate) body_element_id: NodeId,
78 #[allow(unused)]
79 pub(crate) main_element_id: NodeId,
80}
81
82impl DioxusDocument {
83 pub fn new(vdom: VirtualDom, mut config: DocumentConfig) -> Self {
85 set_event_converter(Box::new(NativeConverter {}));
87
88 config.base_url = Some(
89 config
90 .base_url
91 .unwrap_or_else(|| String::from("dioxus://index.html")),
92 );
93 let mut doc = BaseDocument::new(config);
94
95 doc.add_user_agent_stylesheet(DEFAULT_CSS);
97
98 let mut mutr = doc.mutate();
112 let html_element_id = mutr.create_element(qual_name("html", None), vec![]);
113 mutr.append_children(mutr.doc.root_node().id, &[html_element_id]);
114
115 let head_element_id = mutr.create_element(qual_name("head", None), vec![]);
117 mutr.append_children(html_element_id, &[head_element_id]);
118
119 let body_element_id = mutr.create_element(qual_name("body", None), vec![]);
121 mutr.append_children(html_element_id, &[body_element_id]);
122
123 let main_attr = blitz_dom::Attribute {
125 name: qual_name("id", None),
126 value: "main".to_string(),
127 };
128 let main_element_id = mutr.create_element(qual_name("main", None), vec![main_attr]);
129 mutr.append_children(body_element_id, &[main_element_id]);
130
131 drop(mutr);
132
133 let vdom_state = DioxusState::create(main_element_id);
134 Self {
135 vdom,
136 vdom_state,
137 inner: Rc::new(RefCell::new(doc)),
138 html_element_id,
139 head_element_id,
140 body_element_id,
141 main_element_id,
142 }
143 }
144
145 pub fn initial_build(&mut self) {
147 let mut inner = self.inner.borrow_mut();
148 let mut writer = MutationWriter::new(&mut inner, &mut self.vdom_state);
149 self.vdom.rebuild(&mut writer);
150 drop(writer);
151 drop(inner);
152 self.flush_queued_mounted_events();
153 }
154
155 #[doc(hidden)]
158 pub fn create_head_element(
159 &mut self,
160 name: &str,
161 attributes: &[(String, String)],
162 contents: &Option<String>,
163 ) {
164 let mut inner = self.inner.borrow_mut();
165 let mut mutr = inner.mutate();
166
167 let attributes = attributes
168 .iter()
169 .map(|(name, value)| Attribute {
170 name: qual_name(name, None),
171 value: value.clone(),
172 })
173 .collect();
174
175 let new_elem_id = mutr.create_element(qual_name(name, None), attributes);
176 mutr.append_children(self.head_element_id, &[new_elem_id]);
177 if let Some(contents) = contents {
178 let text_node_id = mutr.create_text_node(contents);
179 mutr.append_children(new_elem_id, &[text_node_id]);
180 }
181 }
182
183 pub(crate) fn flush_queued_mounted_events(&mut self) {
184 let mut queued_mounted_events = mem::take(&mut self.vdom_state.queued_mounted_events);
185 for element_id in queued_mounted_events.drain(..) {
186 let node_id = self.vdom_state.element_to_node_id(element_id);
187
188 if self.inner.borrow().get_node(node_id).is_some() {
189 let event = Event::new(
190 Rc::new(PlatformEventData::new(Box::new(NodeHandle {
191 doc: Rc::clone(&self.inner),
192 node_id,
193 }))) as Rc<dyn Any>,
194 false,
195 );
196 self.vdom
197 .runtime()
198 .handle_event("mounted", event, element_id);
199 }
200 }
201
202 self.vdom_state.queued_mounted_events = queued_mounted_events;
203 }
204}
205
206impl Document for DioxusDocument {
208 fn id(&self) -> usize {
209 self.inner.borrow().id()
210 }
211
212 fn inner(&self) -> DocGuard<'_> {
213 DocGuard::RefCell(self.inner.borrow())
214 }
215
216 fn inner_mut(&mut self) -> DocGuardMut<'_> {
217 DocGuardMut::RefCell(self.inner.borrow_mut())
218 }
219
220 fn poll(&mut self, cx: Option<TaskContext>) -> bool {
221 {
222 let fut = self.vdom.wait_for_work();
223 let mut pinned_fut = pin!(fut);
224
225 static NOOP_WAKER: LazyLock<Waker> = LazyLock::new(noop_waker);
226 let mut cx = cx.unwrap_or_else(|| TaskContext::from_waker(&NOOP_WAKER));
227 match pinned_fut.as_mut().poll(&mut cx) {
228 std::task::Poll::Ready(_) => {}
229 std::task::Poll::Pending => return false,
230 }
231 }
232
233 let mut inner = self.inner.borrow_mut();
234 let mut writer = MutationWriter::new(&mut inner, &mut self.vdom_state);
235 self.vdom.render_immediate(&mut writer);
236 drop(writer);
237 drop(inner);
238 self.flush_queued_mounted_events();
239
240 true
241 }
242
243 fn handle_ui_event(&mut self, event: UiEvent) {
244 let handler = DioxusEventHandler {
245 vdom: &mut self.vdom,
246 vdom_state: &mut self.vdom_state,
247 };
248 let mut driver = EventDriver::new(&mut self.inner, handler);
249 driver.handle_ui_event(event);
250 }
251}
252
253pub struct DioxusEventHandler<'v> {
254 vdom: &'v mut VirtualDom,
255 vdom_state: &'v mut DioxusState,
256}
257
258impl EventHandler for DioxusEventHandler<'_> {
259 fn handle_event(
260 &mut self,
261 chain: &[usize],
262 event: &mut DomEvent,
263 doc: &mut dyn Document,
264 event_state: &mut EventState,
265 ) {
266 let event_kind_idx = event.data.discriminant() as usize;
269 let event_kind_count = self.vdom_state.event_handler_counts[event_kind_idx];
270 if event_kind_count == 0 {
271 return;
272 }
273
274 let event_data = match &event.data {
275 DomEventData::PointerMove(mevent)
276 | DomEventData::PointerDown(mevent)
277 | DomEventData::PointerUp(mevent)
278 | DomEventData::PointerCancel(mevent)
279 | DomEventData::PointerLeave(mevent)
280 | DomEventData::PointerEnter(mevent)
281 | DomEventData::PointerOver(mevent)
282 | DomEventData::PointerOut(mevent)
283 | DomEventData::MouseMove(mevent)
284 | DomEventData::MouseDown(mevent)
285 | DomEventData::MouseUp(mevent)
286 | DomEventData::MouseLeave(mevent)
287 | DomEventData::MouseEnter(mevent)
288 | DomEventData::MouseOver(mevent)
289 | DomEventData::MouseOut(mevent)
290 | DomEventData::Click(mevent)
291 | DomEventData::ContextMenu(mevent)
292 | DomEventData::DoubleClick(mevent) => {
293 Some(wrap_event_data(NativePointerData(mevent.clone())))
294 }
295
296 DomEventData::TouchStart(tevent)
297 | DomEventData::TouchMove(tevent)
298 | DomEventData::TouchEnd(tevent)
299 | DomEventData::TouchCancel(tevent) => {
300 Some(wrap_event_data(NativeTouchData(tevent.clone())))
301 }
302
303 DomEventData::Scroll(sevent) => Some(wrap_event_data(NativeScrollData(sevent.clone()))),
304 DomEventData::Wheel(wevent) => Some(wrap_event_data(NativeWheelData(wevent.clone()))),
305
306 DomEventData::Focus(_) => Some(wrap_event_data(NativeFocusData)),
307 DomEventData::Blur(_) => Some(wrap_event_data(NativeFocusData)),
308 DomEventData::FocusIn(_) => Some(wrap_event_data(NativeFocusData)),
309 DomEventData::FocusOut(_) => Some(wrap_event_data(NativeFocusData)),
310
311 DomEventData::KeyDown(kevent)
312 | DomEventData::KeyUp(kevent)
313 | DomEventData::KeyPress(kevent) => {
314 Some(wrap_event_data(BlitzKeyboardData(kevent.clone())))
315 }
316
317 DomEventData::Input(data) => Some(wrap_event_data(NativeFormData {
318 value: data.value.clone(),
319 values: vec![],
320 })),
321
322 DomEventData::Ime(_) => None,
324
325 DomEventData::AppleStandardKeybinding(_) => None,
327 };
328
329 let Some(event_data) = event_data else {
330 return;
331 };
332
333 let dioxus_id = chain
335 .iter()
336 .find_map(|node_id| doc.inner().get_node(*node_id).and_then(get_dioxus_id));
337 let Some(id) = dioxus_id else {
338 return;
339 };
340
341 let dx_event = Event::new(event_data.clone(), event.bubbles);
343 self.vdom
344 .runtime()
345 .handle_event(event.name(), dx_event.clone(), id);
346
347 if !dx_event.default_action_enabled() {
349 event_state.prevent_default();
350 }
351 if !dx_event.propagates() {
352 event_state.stop_propagation();
353 }
354 }
355}