Upgrade to Blitz 0.1.0-alpha.2 (#4227)
* Update to use latest Blitz net traits * Pass url to flush_style_attribute * Add system-fonts feature to dioxus-native * Native: cleanup log output * Make dioxus-native work with Blitz simplified Document and Renderer traits * Native: upgrade to event refactor * Upgrade to blitz 0.1.0-alpha.2 * Add typo exception Signed-off-by: Nico Burns <nico@nicoburns.com> * Ensure root_id is set to <main> element --------- Signed-off-by: Nico Burns <nico@nicoburns.com>
This commit is contained in:
1 parent
e1bd06bda5
commit
51d7affc54
10 files changed
+654
-1222
No files matched your search
@@ -1,29 +1,36 @@
|
||||
//! Integration between Dioxus and Blitz
|
||||
|
||||
use blitz_dom::Attribute;
|
||||
use futures_util::{pin_mut, FutureExt};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::{any::Any, collections::HashMap, rc::Rc, sync::Arc};
|
||||
|
||||
use blitz_dom::{
|
||||
local_name, namespace_url,
|
||||
net::Resource,
|
||||
node::{Attribute, NodeSpecificData},
|
||||
ns, Atom, BaseDocument, ElementNodeData, Node, NodeData, QualName, DEFAULT_CSS,
|
||||
net::Resource, BaseDocument, Document, EventDriver, EventHandler, Node, DEFAULT_CSS,
|
||||
};
|
||||
use blitz_traits::{
|
||||
events::UiEvent, net::NetProvider, ColorScheme, DomEvent, DomEventData, EventState, Viewport,
|
||||
};
|
||||
|
||||
use blitz_traits::{net::NetProvider, ColorScheme, Document, DomEvent, DomEventData, Viewport};
|
||||
use dioxus_core::{ElementId, Event, VirtualDom};
|
||||
use dioxus_html::{set_event_converter, FormValue, PlatformEventData};
|
||||
use futures_util::{pin_mut, FutureExt};
|
||||
use dioxus_html::{set_event_converter, PlatformEventData};
|
||||
|
||||
use super::event_handler::{BlitzKeyboardData, NativeClickData, NativeConverter, NativeFormData};
|
||||
use crate::events::{BlitzKeyboardData, NativeClickData, NativeConverter, NativeFormData};
|
||||
use crate::mutation_writer::{DioxusState, MutationWriter};
|
||||
use crate::qual_name;
|
||||
use crate::NodeId;
|
||||
|
||||
pub(crate) fn qual_name(local_name: &str, namespace: Option<&str>) -> QualName {
|
||||
QualName {
|
||||
prefix: None,
|
||||
ns: namespace.map(Atom::from).unwrap_or(ns!(html)),
|
||||
local: Atom::from(local_name),
|
||||
}
|
||||
fn wrap_event_data<T: Any>(value: T) -> Rc<dyn Any> {
|
||||
Rc::new(PlatformEventData::new(Box::new(value)))
|
||||
}
|
||||
|
||||
/// Get the value of the "dioxus-data-id" attribute parsed aa usize
|
||||
fn get_dioxus_id(node: &Node) -> Option<ElementId> {
|
||||
node.element_data()?
|
||||
.attrs
|
||||
.iter()
|
||||
.find(|attr| *attr.name.local == *"data-dioxus-id")
|
||||
.and_then(|attr| attr.value.parse::<usize>().ok())
|
||||
.map(ElementId)
|
||||
}
|
||||
|
||||
pub struct DioxusDocument {
|
||||
@@ -41,25 +48,106 @@ pub struct DioxusDocument {
|
||||
pub(crate) main_element_id: NodeId,
|
||||
}
|
||||
|
||||
// Implement DocumentLike and required traits for DioxusDocument
|
||||
impl DioxusDocument {
|
||||
pub fn new(vdom: VirtualDom, net_provider: Option<Arc<dyn NetProvider<Resource>>>) -> Self {
|
||||
let viewport = Viewport::new(0, 0, 1.0, ColorScheme::Light);
|
||||
let mut doc = BaseDocument::new(viewport);
|
||||
|
||||
impl AsRef<BaseDocument> for DioxusDocument {
|
||||
fn as_ref(&self) -> &BaseDocument {
|
||||
&self.inner
|
||||
// Set net provider
|
||||
if let Some(net_provider) = net_provider {
|
||||
doc.set_net_provider(net_provider);
|
||||
}
|
||||
|
||||
// Create some minimal HTML to render the app into.
|
||||
|
||||
// Create the html element
|
||||
let mut mutr = doc.mutate();
|
||||
let html_element_id = mutr.create_element(qual_name("html", None), vec![]);
|
||||
mutr.append_children(mutr.doc.root_node().id, &[html_element_id]);
|
||||
|
||||
// Create the body element
|
||||
let head_element_id = mutr.create_element(qual_name("head", None), vec![]);
|
||||
mutr.append_children(html_element_id, &[head_element_id]);
|
||||
|
||||
// Create the body element
|
||||
let body_element_id = mutr.create_element(qual_name("body", None), vec![]);
|
||||
mutr.append_children(html_element_id, &[body_element_id]);
|
||||
|
||||
// Create another virtual element to hold the root <div id="main"></div> under the html element
|
||||
let main_attr = blitz_dom::Attribute {
|
||||
name: qual_name("id", None),
|
||||
value: "main".to_string(),
|
||||
};
|
||||
let main_element_id = mutr.create_element(qual_name("main", None), vec![main_attr]);
|
||||
mutr.append_children(body_element_id, &[main_element_id]);
|
||||
|
||||
drop(mutr);
|
||||
|
||||
// Include default and user-specified stylesheets
|
||||
doc.add_user_agent_stylesheet(DEFAULT_CSS);
|
||||
|
||||
let vdom_state = DioxusState::create(main_element_id);
|
||||
let mut doc = Self {
|
||||
vdom,
|
||||
vdom_state,
|
||||
inner: doc,
|
||||
html_element_id,
|
||||
head_element_id,
|
||||
body_element_id,
|
||||
main_element_id,
|
||||
};
|
||||
|
||||
doc.inner.set_base_url("dioxus://index.html");
|
||||
//doc.initial_build();
|
||||
doc.inner.print_tree();
|
||||
|
||||
doc
|
||||
}
|
||||
}
|
||||
impl AsMut<BaseDocument> for DioxusDocument {
|
||||
fn as_mut(&mut self) -> &mut BaseDocument {
|
||||
&mut self.inner
|
||||
|
||||
pub fn initial_build(&mut self) {
|
||||
let mut writer = MutationWriter::new(&mut self.inner, &mut self.vdom_state);
|
||||
self.vdom.rebuild(&mut writer);
|
||||
}
|
||||
}
|
||||
impl From<DioxusDocument> for BaseDocument {
|
||||
fn from(doc: DioxusDocument) -> BaseDocument {
|
||||
doc.inner
|
||||
|
||||
pub fn create_head_element(
|
||||
&mut self,
|
||||
name: &str,
|
||||
attributes: &[(String, String)],
|
||||
contents: &Option<String>,
|
||||
) {
|
||||
let mut mutr = self.inner.mutate();
|
||||
|
||||
let attributes = attributes
|
||||
.iter()
|
||||
.map(|(name, value)| Attribute {
|
||||
name: qual_name(name, None),
|
||||
value: value.clone(),
|
||||
})
|
||||
.collect();
|
||||
|
||||
let new_elem_id = mutr.create_element(qual_name(name, None), attributes);
|
||||
mutr.append_children(self.head_element_id, &[new_elem_id]);
|
||||
if let Some(contents) = contents {
|
||||
mutr.append_text_to_node(new_elem_id, contents).unwrap();
|
||||
}
|
||||
|
||||
// TODO: set title (we may also wish to have this built-in to the document?)
|
||||
// if name == "title" {
|
||||
// let title = mutr.doc.nodes[new_elem_id].text_content();
|
||||
//
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
// Implement DocumentLike and required traits for DioxusDocument
|
||||
impl Document for DioxusDocument {
|
||||
type Doc = BaseDocument;
|
||||
fn id(&self) -> usize {
|
||||
self.inner.id()
|
||||
}
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn poll(&mut self, mut cx: std::task::Context) -> bool {
|
||||
{
|
||||
@@ -78,487 +166,94 @@ impl Document for DioxusDocument {
|
||||
true
|
||||
}
|
||||
|
||||
fn id(&self) -> usize {
|
||||
self.inner.id()
|
||||
}
|
||||
|
||||
fn handle_event(&mut self, event: &mut DomEvent) {
|
||||
let chain = self.inner.node_chain(event.target);
|
||||
|
||||
fn handle_event(&mut self, event: UiEvent) {
|
||||
set_event_converter(Box::new(NativeConverter {}));
|
||||
|
||||
let renderer_event = event.clone();
|
||||
|
||||
let mut prevent_default = false;
|
||||
let mut stop_propagation = false;
|
||||
|
||||
match &event.data {
|
||||
DomEventData::MouseMove(data)
|
||||
| DomEventData::MouseDown(data)
|
||||
| DomEventData::MouseUp(data) => {
|
||||
let click_event_data = wrap_event_data(NativeClickData(data.clone()));
|
||||
|
||||
for node_id in chain.clone().into_iter() {
|
||||
let node = &self.inner.tree()[node_id];
|
||||
let dioxus_id = node.element_data().and_then(DioxusDocument::dioxus_id);
|
||||
|
||||
if let Some(id) = dioxus_id {
|
||||
let click_event = Event::new(click_event_data.clone(), true);
|
||||
self.vdom
|
||||
.runtime()
|
||||
.handle_event(event.name(), click_event.clone(), id);
|
||||
prevent_default |= !click_event.default_action_enabled();
|
||||
stop_propagation |= !click_event.propagates();
|
||||
}
|
||||
|
||||
if !event.bubbles || stop_propagation {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DomEventData::Click(data) => {
|
||||
let click_event_data = wrap_event_data(NativeClickData(data.clone()));
|
||||
|
||||
for node_id in chain.clone().into_iter() {
|
||||
let node = &self.inner.tree()[node_id];
|
||||
let dioxus_id = node.element_data().and_then(DioxusDocument::dioxus_id);
|
||||
let mut trigger_label = false;
|
||||
|
||||
if let Some(id) = dioxus_id {
|
||||
// Trigger click event
|
||||
let click_event = Event::new(click_event_data.clone(), true);
|
||||
self.vdom
|
||||
.runtime()
|
||||
.handle_event("click", click_event.clone(), id);
|
||||
prevent_default |= !click_event.default_action_enabled();
|
||||
stop_propagation |= !click_event.propagates();
|
||||
|
||||
if !prevent_default {
|
||||
let mut default_event =
|
||||
DomEvent::new(node_id, renderer_event.data.clone());
|
||||
self.inner.as_mut().handle_event(&mut default_event);
|
||||
prevent_default = true;
|
||||
|
||||
let element = self.inner.tree()[node_id].element_data().unwrap();
|
||||
trigger_label = element.name.local == *"label";
|
||||
|
||||
//TODO Check for other inputs which trigger input event on click here, eg radio
|
||||
let triggers_input_event = element.name.local == local_name!("input")
|
||||
&& element.attr(local_name!("type")) == Some("checkbox");
|
||||
if triggers_input_event {
|
||||
let form_data =
|
||||
wrap_event_data(self.input_event_form_data(&chain, element));
|
||||
let input_event = Event::new(form_data, true);
|
||||
self.vdom.runtime().handle_event("input", input_event, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Clicking labels triggers click, and possibly input event, of bound input
|
||||
if trigger_label {
|
||||
if let Some((dioxus_id, node_id)) = self.label_bound_input_element(node_id)
|
||||
{
|
||||
let click_event = Event::new(click_event_data.clone(), true);
|
||||
self.vdom.runtime().handle_event(
|
||||
"click",
|
||||
click_event.clone(),
|
||||
dioxus_id,
|
||||
);
|
||||
|
||||
// Handle default DOM event
|
||||
if click_event.default_action_enabled() {
|
||||
let DomEventData::Click(event) = &renderer_event.data else {
|
||||
unreachable!();
|
||||
};
|
||||
let input_click_data = self
|
||||
.inner
|
||||
.get_node(node_id)
|
||||
.unwrap()
|
||||
.synthetic_click_event(event.mods);
|
||||
let mut default_event = DomEvent::new(node_id, input_click_data);
|
||||
self.inner.as_mut().handle_event(&mut default_event);
|
||||
prevent_default = true;
|
||||
|
||||
// TODO: Generated click events should bubble separately
|
||||
// prevent_default |= !click_event.default_action_enabled();
|
||||
|
||||
//TODO Check for other inputs which trigger input event on click here, eg radio
|
||||
let element_data = self
|
||||
.inner
|
||||
.get_node(node_id)
|
||||
.unwrap()
|
||||
.element_data()
|
||||
.unwrap();
|
||||
let triggers_input_event =
|
||||
element_data.attr(local_name!("type")) == Some("checkbox");
|
||||
let form_data = wrap_event_data(
|
||||
self.input_event_form_data(&chain, element_data),
|
||||
);
|
||||
if triggers_input_event {
|
||||
let input_event = Event::new(form_data, true);
|
||||
self.vdom.runtime().handle_event(
|
||||
"input",
|
||||
input_event,
|
||||
dioxus_id,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !event.bubbles || stop_propagation {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DomEventData::KeyPress(kevent) => {
|
||||
let key_event_data = wrap_event_data(BlitzKeyboardData(kevent.clone()));
|
||||
|
||||
for node_id in chain.clone().into_iter() {
|
||||
let node = &self.inner.tree()[node_id];
|
||||
let dioxus_id = node.element_data().and_then(DioxusDocument::dioxus_id);
|
||||
println!("{} {:?}", node_id, dioxus_id);
|
||||
|
||||
if let Some(id) = dioxus_id {
|
||||
if kevent.state.is_pressed() {
|
||||
// Handle keydown event
|
||||
let event = Event::new(key_event_data.clone(), true);
|
||||
self.vdom
|
||||
.runtime()
|
||||
.handle_event("keydown", event.clone(), id);
|
||||
prevent_default |= !event.default_action_enabled();
|
||||
stop_propagation |= !event.propagates();
|
||||
|
||||
if !prevent_default && kevent.text.is_some() {
|
||||
// Handle keypress event
|
||||
let event = Event::new(key_event_data.clone(), true);
|
||||
self.vdom
|
||||
.runtime()
|
||||
.handle_event("keypress", event.clone(), id);
|
||||
prevent_default |= !event.default_action_enabled();
|
||||
stop_propagation |= !event.propagates();
|
||||
|
||||
if !prevent_default {
|
||||
// Handle default DOM event
|
||||
let mut default_event =
|
||||
DomEvent::new(node_id, renderer_event.data.clone());
|
||||
self.inner.as_mut().handle_event(&mut default_event);
|
||||
prevent_default = true;
|
||||
|
||||
// Handle input event
|
||||
let element =
|
||||
self.inner.tree()[node_id].element_data().unwrap();
|
||||
let triggers_input_event = &element.name.local == "input"
|
||||
&& matches!(
|
||||
element.attr(local_name!("type")),
|
||||
None | Some("text" | "password" | "email" | "search")
|
||||
);
|
||||
if triggers_input_event {
|
||||
let form_data = wrap_event_data(dbg!(
|
||||
self.input_event_form_data(&chain, element)
|
||||
));
|
||||
let input_event = Event::new(form_data, true);
|
||||
self.vdom.runtime().handle_event("input", input_event, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Handle keyup event
|
||||
let event = Event::new(key_event_data.clone(), true);
|
||||
self.vdom.runtime().handle_event("keyup", event.clone(), id);
|
||||
prevent_default |= !event.default_action_enabled();
|
||||
stop_propagation |= !event.propagates();
|
||||
}
|
||||
}
|
||||
|
||||
if !event.bubbles || stop_propagation {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DomEventData::Hover => {}
|
||||
|
||||
// TODO: Implement IME and Hover events handling
|
||||
DomEventData::Ime(_) => {}
|
||||
}
|
||||
|
||||
if !event.cancelable || !prevent_default {
|
||||
self.inner.as_mut().handle_event(event);
|
||||
}
|
||||
let handler = DioxusEventHandler {
|
||||
vdom: &mut self.vdom,
|
||||
vdom_state: &mut self.vdom_state,
|
||||
};
|
||||
let mut driver = EventDriver::new(self.inner.mutate(), handler);
|
||||
driver.handle_ui_event(event);
|
||||
}
|
||||
}
|
||||
|
||||
fn wrap_event_data<T: Any>(value: T) -> Rc<dyn Any> {
|
||||
Rc::new(PlatformEventData::new(Box::new(value)))
|
||||
impl Deref for DioxusDocument {
|
||||
type Target = BaseDocument;
|
||||
fn deref(&self) -> &BaseDocument {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
impl DerefMut for DioxusDocument {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
impl From<DioxusDocument> for BaseDocument {
|
||||
fn from(doc: DioxusDocument) -> BaseDocument {
|
||||
doc.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl DioxusDocument {
|
||||
/// Generate the FormData from an input event
|
||||
/// Currently only cares about input checkboxes
|
||||
pub fn input_event_form_data(
|
||||
&self,
|
||||
parent_chain: &[usize],
|
||||
element_node_data: &ElementNodeData,
|
||||
) -> NativeFormData {
|
||||
let parent_form = parent_chain.iter().find_map(|id| {
|
||||
let node = self.inner.get_node(*id)?;
|
||||
let element_data = node.element_data()?;
|
||||
if element_data.name.local == local_name!("form") {
|
||||
Some(node)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
let values = if let Some(parent_form) = parent_form {
|
||||
let mut values = HashMap::<String, FormValue>::new();
|
||||
for form_input in self.input_descendents(parent_form).into_iter() {
|
||||
// Match html behaviour here. To be included in values:
|
||||
// - input must have a name
|
||||
// - if its an input, we only include it if checked
|
||||
// - if value is not specified, it defaults to 'on'
|
||||
if let Some(name) = form_input.attr(local_name!("name")) {
|
||||
if form_input.attr(local_name!("type")) == Some("checkbox")
|
||||
&& form_input
|
||||
.element_data()
|
||||
.and_then(|data| data.checkbox_input_checked())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
let value = form_input
|
||||
.attr(local_name!("value"))
|
||||
.unwrap_or("on")
|
||||
.to_string();
|
||||
values.insert(name.to_string(), FormValue(vec![value]));
|
||||
}
|
||||
}
|
||||
}
|
||||
values
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
let value = match &element_node_data.node_specific_data {
|
||||
NodeSpecificData::CheckboxInput(checked) => checked.to_string(),
|
||||
NodeSpecificData::TextInput(input_data) => input_data.editor.text().to_string(),
|
||||
_ => element_node_data
|
||||
.attr(local_name!("value"))
|
||||
.unwrap_or_default()
|
||||
.to_string(),
|
||||
};
|
||||
pub struct DioxusEventHandler<'v> {
|
||||
vdom: &'v mut VirtualDom,
|
||||
#[allow(dead_code, reason = "WIP")]
|
||||
vdom_state: &'v mut DioxusState,
|
||||
}
|
||||
|
||||
NativeFormData { value, values }
|
||||
}
|
||||
|
||||
/// Collect all the inputs which are descendents of a given node
|
||||
fn input_descendents(&self, node: &Node) -> Vec<&Node> {
|
||||
node.children
|
||||
.iter()
|
||||
.flat_map(|id| {
|
||||
let mut res = Vec::<&Node>::new();
|
||||
let Some(n) = self.inner.get_node(*id) else {
|
||||
return res;
|
||||
};
|
||||
let Some(element_data) = n.element_data() else {
|
||||
return res;
|
||||
};
|
||||
if element_data.name.local == local_name!("input") {
|
||||
res.push(n);
|
||||
}
|
||||
res.extend(self.input_descendents(n).iter());
|
||||
res
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
vdom: VirtualDom,
|
||||
net_provider: Option<Arc<dyn NetProvider<Data = Resource>>>,
|
||||
) -> Self {
|
||||
let viewport = Viewport::new(0, 0, 1.0, ColorScheme::Light);
|
||||
let mut doc = BaseDocument::new(viewport);
|
||||
|
||||
// Set net provider
|
||||
if let Some(net_provider) = net_provider {
|
||||
doc.set_net_provider(net_provider);
|
||||
}
|
||||
|
||||
// Create a virtual "html" element to act as the root element, as we won't necessarily
|
||||
// have a single root otherwise, while the rest of blitz requires that we do
|
||||
let html_element_id = doc.create_node(NodeData::Element(ElementNodeData::new(
|
||||
qual_name("html", None),
|
||||
Vec::new(),
|
||||
)));
|
||||
let root_node_id = doc.root_node().id;
|
||||
let html_element = doc.get_node_mut(html_element_id).unwrap();
|
||||
html_element.parent = Some(root_node_id);
|
||||
let root_node = doc.get_node_mut(root_node_id).unwrap();
|
||||
root_node.children.push(html_element_id);
|
||||
|
||||
// Create the body element
|
||||
let body_element_id = doc.create_node(NodeData::Element(ElementNodeData::new(
|
||||
qual_name("body", None),
|
||||
Vec::new(),
|
||||
)));
|
||||
let html_element = doc.get_node_mut(html_element_id).unwrap();
|
||||
html_element.children.push(body_element_id);
|
||||
let body_element = doc.get_node_mut(body_element_id).unwrap();
|
||||
body_element.parent = Some(html_element_id);
|
||||
|
||||
// Create another virtual element to hold the root <div id="main"></div> under the html element
|
||||
let main_element_id = doc.create_node(NodeData::Element(ElementNodeData::new(
|
||||
qual_name("div", Some("id")),
|
||||
vec![blitz_dom::node::Attribute {
|
||||
name: qual_name("id", None),
|
||||
value: "main".to_string(),
|
||||
}],
|
||||
)));
|
||||
let body_element = doc.get_node_mut(body_element_id).unwrap();
|
||||
body_element.children.push(main_element_id);
|
||||
let main_element = doc.get_node_mut(main_element_id).unwrap();
|
||||
main_element.parent = Some(body_element_id);
|
||||
|
||||
// Create the head element
|
||||
let head_element_id = doc.create_node(NodeData::Element(ElementNodeData::new(
|
||||
qual_name("head", None),
|
||||
Vec::new(),
|
||||
)));
|
||||
let head_element = doc.get_node_mut(head_element_id).unwrap();
|
||||
head_element.parent = Some(html_element_id);
|
||||
let html_element = doc.get_node_mut(html_element_id).unwrap();
|
||||
html_element.children.push(head_element_id);
|
||||
|
||||
// Include default and user-specified stylesheets
|
||||
doc.add_user_agent_stylesheet(DEFAULT_CSS);
|
||||
|
||||
let state = DioxusState::create(&mut doc, main_element_id);
|
||||
let mut doc = Self {
|
||||
vdom,
|
||||
vdom_state: state,
|
||||
inner: doc,
|
||||
html_element_id,
|
||||
head_element_id,
|
||||
body_element_id,
|
||||
main_element_id,
|
||||
};
|
||||
|
||||
doc.inner.set_base_url("dioxus://index.html");
|
||||
|
||||
// doc.initial_build();
|
||||
|
||||
// doc.inner.print_tree();
|
||||
|
||||
doc
|
||||
}
|
||||
|
||||
pub fn initial_build(&mut self) {
|
||||
// let mut writer = MutationWriter::new(&mut self.inner, &mut self.vdom_state);
|
||||
|
||||
// self.vdom.rebuild(&mut writer);
|
||||
// dbg!(self.vdom.rebuild_to_vec());
|
||||
// std::process::exit(0);
|
||||
// dbg!(writer.state);
|
||||
}
|
||||
|
||||
pub fn label_bound_input_element(&self, label_node_id: NodeId) -> Option<(ElementId, NodeId)> {
|
||||
let bound_input_elements = self.inner.label_bound_input_elements(label_node_id);
|
||||
|
||||
// Filter down bound elements to those which have dioxus id
|
||||
bound_input_elements.into_iter().find_map(|n| {
|
||||
let target_element_data = n.element_data()?;
|
||||
let node_id = n.id;
|
||||
let dioxus_id = DioxusDocument::dioxus_id(target_element_data)?;
|
||||
Some((dioxus_id, node_id))
|
||||
})
|
||||
}
|
||||
|
||||
fn dioxus_id(element_node_data: &ElementNodeData) -> Option<ElementId> {
|
||||
Some(ElementId(
|
||||
element_node_data
|
||||
.attrs
|
||||
.iter()
|
||||
.find(|attr| *attr.name.local == *"data-dioxus-id")?
|
||||
.value
|
||||
.parse::<usize>()
|
||||
.ok()?,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn create_head_element(
|
||||
impl EventHandler for DioxusEventHandler<'_> {
|
||||
fn handle_event(
|
||||
&mut self,
|
||||
name: &str,
|
||||
attributes: &[(String, String)],
|
||||
contents: &Option<String>,
|
||||
chain: &[usize],
|
||||
event: &mut DomEvent,
|
||||
mutr: &mut blitz_dom::DocumentMutator<'_>,
|
||||
event_state: &mut EventState,
|
||||
) {
|
||||
let mut stylesheet = None;
|
||||
let mut title = None;
|
||||
if name == "link" {
|
||||
let is_stylesheet = attributes
|
||||
.iter()
|
||||
.any(|(name, value)| name == "rel" && value == "stylesheet");
|
||||
if is_stylesheet {
|
||||
stylesheet = attributes
|
||||
.iter()
|
||||
.find(|(name, _value)| name == "href")
|
||||
.map(|(_name, value)| value.clone());
|
||||
let event_data = match &event.data {
|
||||
DomEventData::MouseMove(mevent)
|
||||
| DomEventData::MouseDown(mevent)
|
||||
| DomEventData::MouseUp(mevent)
|
||||
| DomEventData::Click(mevent) => Some(wrap_event_data(NativeClickData(mevent.clone()))),
|
||||
|
||||
DomEventData::KeyDown(kevent)
|
||||
| DomEventData::KeyUp(kevent)
|
||||
| DomEventData::KeyPress(kevent) => {
|
||||
Some(wrap_event_data(BlitzKeyboardData(kevent.clone())))
|
||||
}
|
||||
|
||||
DomEventData::Input(data) => Some(wrap_event_data(NativeFormData {
|
||||
value: data.value.clone(),
|
||||
values: HashMap::new(),
|
||||
})),
|
||||
|
||||
// TODO: Implement IME handling
|
||||
DomEventData::Ime(_) => None,
|
||||
};
|
||||
|
||||
let Some(event_data) = event_data else {
|
||||
return;
|
||||
};
|
||||
|
||||
for &node_id in chain {
|
||||
// Get dioxus vdom id for node
|
||||
let dioxus_id = mutr.doc.get_node(node_id).and_then(get_dioxus_id);
|
||||
let Some(id) = dioxus_id else {
|
||||
return;
|
||||
};
|
||||
|
||||
// Handle event in vdom
|
||||
let dx_event = Event::new(event_data.clone(), event.bubbles);
|
||||
self.vdom
|
||||
.runtime()
|
||||
.handle_event(event.name(), dx_event.clone(), id);
|
||||
|
||||
// Update event state
|
||||
if !dx_event.default_action_enabled() {
|
||||
event_state.prevent_default();
|
||||
}
|
||||
if !dx_event.propagates() {
|
||||
event_state.stop_propagation();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if name == "title" {
|
||||
title = attributes
|
||||
.iter()
|
||||
.find(|(name, _value)| name == "text")
|
||||
.and_then(|(_name, _value)| contents.clone());
|
||||
}
|
||||
|
||||
let attributes = attributes
|
||||
.iter()
|
||||
.map(|(name, value)| Attribute {
|
||||
name: qual_name(name, None),
|
||||
value: value.clone(),
|
||||
})
|
||||
.collect();
|
||||
|
||||
let new_element = self
|
||||
.inner
|
||||
.create_node(NodeData::Element(ElementNodeData::new(
|
||||
qual_name(name, None),
|
||||
attributes,
|
||||
)));
|
||||
|
||||
if let Some(contents) = contents {
|
||||
let text_node = self.inner.create_text_node(contents);
|
||||
self.inner
|
||||
.get_node_mut(new_element)
|
||||
.unwrap()
|
||||
.children
|
||||
.push(text_node);
|
||||
}
|
||||
|
||||
self.inner
|
||||
.get_node_mut(self.head_element_id)
|
||||
.unwrap()
|
||||
.children
|
||||
.push(new_element);
|
||||
|
||||
if let Some(url) = stylesheet {
|
||||
crate::assets::fetch_linked_stylesheet(&self.inner, new_element, url);
|
||||
}
|
||||
|
||||
if let Some(_title) = title {
|
||||
println!("todo: set title");
|
||||
}
|
||||
}
|
||||
|
||||
// pub fn apply_mutations(&mut self) {
|
||||
// // Apply the mutations to the actual dom
|
||||
// let mut writer = MutationWriter {
|
||||
// doc: &mut self.inner,
|
||||
// state: &mut self.vdom_state,
|
||||
// };
|
||||
// self.vdom.render_immediate(&mut writer);
|
||||
|
||||
// println!("APPLY MUTATIONS");
|
||||
// self.inner.print_tree();
|
||||
// }
|
||||
}
|
||||
Reference in new issue
Block a user