Merge branch 'master' into implement-file-engine

This commit is contained in:
Jonathan Kelley committed 2023-04-26 15:32:34 -07:00
commit 22cfff647c
12 files changed
+259 -61

No files matched your search

+1 -1
View File
@@ -11,7 +11,7 @@ fn App(cx: Scope) -> Element {
cx.render(rsx! {
input {
r#type: "file",
accept: ".txt",
accept: ".txt, .rs",
multiple: true,
onchange: |evt| {
to_owned![files_uploaded];
+16 -3
View File
@@ -1,3 +1,5 @@
use std::fmt::Display;
use dioxus::prelude::*;
fn main() {
@@ -5,9 +7,20 @@ fn main() {
}
fn app(cx: Scope) -> Element {
cx.render(rsx! { generic_child::<i32>{} })
cx.render(rsx! { generic_child {
data: 0i32
} })
}
fn generic_child<T>(cx: Scope) -> Element {
cx.render(rsx! { div {} })
#[derive(PartialEq, Props)]
struct GenericChildProps<T: Display + PartialEq> {
data: T,
}
fn generic_child<T: Display + PartialEq>(cx: Scope<GenericChildProps<T>>) -> Element {
let data = &cx.props.data;
cx.render(rsx! { div {
"{data}"
} })
}
+6 -7
View File
@@ -276,13 +276,12 @@ impl Writer<'_> {
let start = location.start();
let line_start = start.line - 1;
let this_line = self.src[line_start];
let beginning = if this_line.len() > start.column {
this_line[..start.column].trim()
} else {
""
};
let beginning = self
.src
.get(line_start)
.filter(|this_line| this_line.len() > start.column)
.map(|this_line| this_line[..start.column].trim())
.unwrap_or_default();
beginning.is_empty()
}
+59 -37
View File
@@ -384,51 +384,73 @@ impl VirtualDom {
data,
};
// Loop through each dynamic attribute in this template before moving up to the template's parent.
while let Some(el_ref) = parent_path {
// safety: we maintain references of all vnodes in the element slab
let template = unsafe { el_ref.template.unwrap().as_ref() };
let node_template = template.template.get();
let target_path = el_ref.path;
// If the event bubbles, we traverse through the tree until we find the target element.
if bubbles {
// Loop through each dynamic attribute (in a depth first order) in this template before moving up to the template's parent.
while let Some(el_ref) = parent_path {
// safety: we maintain references of all vnodes in the element slab
let template = unsafe { el_ref.template.unwrap().as_ref() };
let node_template = template.template.get();
let target_path = el_ref.path;
for (idx, attr) in template.dynamic_attrs.iter().enumerate() {
let this_path = node_template.attr_paths[idx];
for (idx, attr) in template.dynamic_attrs.iter().enumerate() {
let this_path = node_template.attr_paths[idx];
// Remove the "on" prefix if it exists, TODO, we should remove this and settle on one
if attr.name.trim_start_matches("on") == name
&& target_path.is_decendant(&this_path)
{
listeners.push(&attr.value);
// Remove the "on" prefix if it exists, TODO, we should remove this and settle on one
if attr.name.trim_start_matches("on") == name
&& target_path.is_decendant(&this_path)
{
listeners.push(&attr.value);
// Break if the event doesn't bubble anyways
if !bubbles {
break;
// Break if this is the exact target element.
// This means we won't call two listeners with the same name on the same element. This should be
// documented, or be rejected from the rsx! macro outright
if target_path == this_path {
break;
}
}
}
// Break if this is the exact target element.
// This means we won't call two listeners with the same name on the same element. This should be
// documented, or be rejected from the rsx! macro outright
if target_path == this_path {
break;
// Now that we've accumulated all the parent attributes for the target element, call them in reverse order
// We check the bubble state between each call to see if the event has been stopped from bubbling
for listener in listeners.drain(..).rev() {
if let AttributeValue::Listener(listener) = listener {
if let Some(cb) = listener.borrow_mut().as_deref_mut() {
cb(uievent.clone());
}
if !uievent.propagates.get() {
return;
}
}
}
parent_path = template.parent.and_then(|id| self.elements.get(id.0));
}
} else {
// Otherwise, we just call the listener on the target element
if let Some(el_ref) = parent_path {
// safety: we maintain references of all vnodes in the element slab
let template = unsafe { el_ref.template.unwrap().as_ref() };
let node_template = template.template.get();
let target_path = el_ref.path;
for (idx, attr) in template.dynamic_attrs.iter().enumerate() {
let this_path = node_template.attr_paths[idx];
// Remove the "on" prefix if it exists, TODO, we should remove this and settle on one
// Only call the listener if this is the exact target element.
if attr.name.trim_start_matches("on") == name && target_path == this_path {
if let AttributeValue::Listener(listener) = &attr.value {
if let Some(cb) = listener.borrow_mut().as_deref_mut() {
cb(uievent.clone());
}
break;
}
}
}
}
// Now that we've accumulated all the parent attributes for the target element, call them in reverse order
// We check the bubble state between each call to see if the event has been stopped from bubbling
for listener in listeners.drain(..).rev() {
if let AttributeValue::Listener(listener) = listener {
if let Some(cb) = listener.borrow_mut().as_deref_mut() {
cb(uievent.clone());
}
if !uievent.propagates.get() {
return;
}
}
}
parent_path = template.parent.and_then(|id| self.elements.get(id.0));
}
}
+99 -6
View File
@@ -167,10 +167,10 @@ class Interpreter {
}
break;
case "checked":
node.checked = value === "true";
node.checked = value === "true" || value === true;
break;
case "selected":
node.selected = value === "true";
node.selected = value === "true" || value === true;
break;
case "dangerous_inner_html":
node.innerHTML = value;
@@ -344,11 +344,104 @@ class Interpreter {
this.RemoveEventListener(edit.id, edit.name);
break;
case "NewEventListener":
const bubbles = event_bubbles(edit.name);
let bubbles = event_bubbles(edit.name);
this.NewEventListener(edit.name, edit.id, bubbles, (event) =>
handler(event, edit.name, bubbles)
);
// this handler is only provided on desktop implementations since this
// method is not used by the web implementation
let handler = (event) => {
let target = event.target;
if (target != null) {
let realId = target.getAttribute(`data-dioxus-id`);
let shouldPreventDefault = target.getAttribute(
`dioxus-prevent-default`
);
if (event.type === "click") {
// todo call prevent default if it's the right type of event
let a_element = target.closest("a");
if (a_element != null) {
event.preventDefault();
if (
shouldPreventDefault !== `onclick` &&
a_element.getAttribute(`dioxus-prevent-default`) !== `onclick`
) {
const href = a_element.getAttribute("href");
if (href !== "" && href !== null && href !== undefined) {
window.ipc.postMessage(
serializeIpcMessage("browser_open", { href })
);
}
}
}
// also prevent buttons from submitting
if (target.tagName === "BUTTON" && event.type == "submit") {
event.preventDefault();
}
}
// walk the tree to find the real element
while (realId == null) {
// we've reached the root we don't want to send an event
if (target.parentElement === null) {
return;
}
target = target.parentElement;
realId = target.getAttribute(`data-dioxus-id`);
}
shouldPreventDefault = target.getAttribute(
`dioxus-prevent-default`
);
let contents = serialize_event(event);
if (shouldPreventDefault === `on${event.type}`) {
event.preventDefault();
}
if (event.type === "submit") {
event.preventDefault();
}
if (
target.tagName === "FORM" &&
(event.type === "submit" || event.type === "input")
) {
for (let x = 0; x < target.elements.length; x++) {
let element = target.elements[x];
let name = element.getAttribute("name");
if (name != null) {
if (element.getAttribute("type") === "checkbox") {
// @ts-ignore
contents.values[name] = element.checked ? "true" : "false";
} else if (element.getAttribute("type") === "radio") {
if (element.checked) {
contents.values[name] = element.value;
}
} else {
// @ts-ignore
contents.values[name] =
element.value ?? element.textContent;
}
}
}
}
if (realId === null) {
return;
}
window.ipc.postMessage(
serializeIpcMessage("user_event", {
name: edit.name,
element: parseInt(realId),
data: contents,
bubbles,
})
);
}
};
this.NewEventListener(edit.name, edit.id, bubbles, handler);
break;
}
}
+1 -1
View File
@@ -18,7 +18,7 @@ futures-util = { version = "0.3.25", default-features = false, features = [
"sink",
] }
futures-channel = { version = "0.3.25", features = ["sink"] }
tokio = { version = "1.22.0", features = ["time"] }
tokio = { version = "1.22.0", features = ["time", "macros"] }
tokio-stream = { version = "0.1.11", features = ["net"] }
tokio-util = { version = "0.7.4", features = ["rt"] }
serde = { version = "1.0.151", features = ["derive"] }
-1
View File
@@ -29,7 +29,6 @@ lightningcss = "1.0.0-alpha.39"
rayon = "1.6.1"
shipyard = { version = "0.6.2", features = ["proc", "std"], default-features = false }
shipyard_hierarchy = "0.6.0"
[dev-dependencies]
rand = "0.8.5"
+2 -1
View File
@@ -98,7 +98,8 @@ pub fn render<R: Driver>(
let event_tx_clone = event_tx.clone();
if !cfg.headless {
std::thread::spawn(move || {
let tick_rate = Duration::from_millis(1000);
// Timeout after 10ms when waiting for events
let tick_rate = Duration::from_millis(10);
loop {
if crossterm::event::poll(tick_rate).unwrap() {
let evt = crossterm::event::read().unwrap();
+3 -3
View File
@@ -45,13 +45,13 @@ pub fn Route<'a>(cx: Scope<'a, RouteProps<'a>>) -> Element {
router_root.register_total_route(route_context.total_route, cx.scope_id());
});
log::debug!("Checking Route: {:?}", cx.props.to);
log::trace!("Checking Route: {:?}", cx.props.to);
if router_root.should_render(cx.scope_id()) {
log::debug!("Route should render: {:?}", cx.scope_id());
log::trace!("Route should render: {:?}", cx.scope_id());
cx.render(rsx!(&cx.props.children))
} else {
log::debug!("Route should *not* render: {:?}", cx.scope_id());
log::trace!("Route should *not* render: {:?}", cx.scope_id());
cx.render(rsx!(()))
}
}
+3
View File
@@ -25,3 +25,6 @@ convert_case = "0.5.0"
# default = ["html"]
# eventually more output options
[dev-dependencies]
pretty_assertions = "1.2.1"
+68
View File
@@ -0,0 +1,68 @@
use html_parser::Dom;
#[test]
fn simple_elements() {
let html = r#"
<div>
<div class="asd">hello world!</div>
<div id="asd">hello world!</div>
<div id="asd">hello world!</div>
<div for="asd">hello world!</div>
<div async="asd">hello world!</div>
<div LargeThing="asd">hello world!</div>
<ai-is-awesome>hello world!</ai-is-awesome>
</div>
"#
.trim();
let dom = Dom::parse(html).unwrap();
let body = rsx_rosetta::rsx_from_html(&dom);
let out = dioxus_autofmt::write_block_out(body).unwrap();
let expected = r#"
div {
div { class: "asd", "hello world!" }
div { id: "asd", "hello world!" }
div { id: "asd", "hello world!" }
div { r#for: "asd", "hello world!" }
div { r#async: "asd", "hello world!" }
div { large_thing: "asd", "hello world!" }
ai_is_awesome { "hello world!" }
}"#;
pretty_assertions::assert_eq!(&out, &expected);
}
#[test]
fn deeply_nested() {
let html = r#"
<div>
<div class="asd">
<div class="asd">
<div class="asd">
<div class="asd">
</div>
</div>
</div>
</div>
</div>
"#
.trim();
let dom = Dom::parse(html).unwrap();
let body = rsx_rosetta::rsx_from_html(&dom);
let out = dioxus_autofmt::write_block_out(body).unwrap();
let expected = r#"
div {
div { class: "asd",
div { class: "asd",
div { class: "asd", div { class: "asd" } }
}
}
}"#;
pretty_assertions::assert_eq!(&out, &expected);
}
+1 -1
View File
@@ -226,7 +226,7 @@ pub async fn run_with_props<T: 'static>(root: fn(Scope<T>) -> Element, root_prop
websys_dom.mount();
loop {
log::debug!("waiting for work");
log::trace!("waiting for work");
// if virtualdom has nothing, wait for it to have something before requesting idle time
// if there is work then this future resolves immediately.