From fdd9a591d482cd2f4c33841c759512c9d107c9a0 Mon Sep 17 00:00:00 2001 From: Nico Burns Date: Wed, 19 Jun 2024 13:31:29 +1200 Subject: [PATCH 1/3] Add basic CI checks (#68) * Add basic CI checks * Fix most clippy lints * Comment out stylo test * Remove useless loop * cargo fmt * Test entire workspace * Further clippy fixes * Test fixes * Further clippy fix --- .github/workflows/ci.yml | 71 ++++ examples/dom_only.rs | 5 - examples/font.rs | 1 - examples/tailwind.rs | 2 +- packages/blitz/src/render.rs | 13 +- packages/blitz/src/viewport.rs | 22 +- .../src/documents/dioxus_document.rs | 35 +- .../src/documents/html_document.rs | 6 +- packages/dioxus-blitz/src/window.rs | 8 +- packages/dom/src/document.rs | 17 + packages/dom/src/htmlsink.rs | 5 +- packages/dom/src/layout/mod.rs | 14 +- packages/dom/src/stylo.rs | 3 +- packages/dom/src/stylo_to_parley.rs | 2 +- packages/dom/src/util.rs | 18 +- tests/stylo_usage.rs | 315 +++++++++--------- 16 files changed, 304 insertions(+), 233 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 examples/dom_only.rs delete mode 100644 examples/font.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..e5196cb74 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,71 @@ +on: + pull_request: + push: + branches: + - main + +name: CI + +env: + RUSTDOCFLAGS: "-D warnings" + CARGO_REGISTRIES_CRATES_IO_PROTOCOL: "sparse" + +jobs: + + # MSRV check. + # Blitz only guarantees "latest stable". However we have this check here to ensure that we advertise + # our MSRV. We also make an effort not to increase MSRV in patch versions of Blitz. + # + # We only run `cargo build` (not `cargo test`) so as to avoid requiring dev-dependencies to build with the MSRV + # version. Building is likely sufficient as runtime errors varying between rust versions is very unlikely. + build-msrv: + name: "MSRV Build [Rust 1.79]" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: 1.79 + - run: sudo apt install libgtk-3-dev libxdo-dev + - run: cargo build --workspace + + test-features-default: + name: "Test [default features]" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - run: sudo apt install libgtk-3-dev libxdo-dev + - run: cargo build --workspace + - run: cargo test --workspace + + fmt: + name: Rustfmt + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: stable + components: rustfmt + - run: cargo fmt --all --check + + clippy: + name: Clippy + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: nightly + components: clippy + - run: sudo apt install libgtk-3-dev libxdo-dev + - run: cargo clippy --workspace -- -D warnings + + doc: + name: Documentation + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - run: cargo doc diff --git a/examples/dom_only.rs b/examples/dom_only.rs deleted file mode 100644 index 71265007b..000000000 --- a/examples/dom_only.rs +++ /dev/null @@ -1,5 +0,0 @@ -use blitz_dom::Document; - -fn main() { - let document = Document::new(); -} diff --git a/examples/font.rs b/examples/font.rs deleted file mode 100644 index 8b1378917..000000000 --- a/examples/font.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/examples/tailwind.rs b/examples/tailwind.rs index 73508c870..807606d9b 100644 --- a/examples/tailwind.rs +++ b/examples/tailwind.rs @@ -13,7 +13,7 @@ fn main() { fn app() -> Element { rsx! { style { {CSS} } - for row in 0..3 { + for _row in 0..3 { div { class: "flex flex-row", div { id: "cool", "h123456789asdjkahskj\nhiiiii" } p { class: "cool", "hi" } diff --git a/packages/blitz/src/render.rs b/packages/blitz/src/render.rs index 37040a9a7..dc246236c 100644 --- a/packages/blitz/src/render.rs +++ b/packages/blitz/src/render.rs @@ -141,19 +141,22 @@ where .await .expect("Error creating surface"); - let default_threads = || -> Option { - #[cfg(target_arch = "macos")] + const DEFAULT_THREADS: Option = { + #[cfg(target_os = "macos")] { - Some(NonZeroUsize::new(1)?) + NonZeroUsize::new(1) + } + #[cfg(not(target_os = "macos"))] + { + None } - None }; let options = RendererOptions { surface_format: Some(surface.config.format), antialiasing_support: AaSupport::all(), use_cpu: false, - num_init_threads: default_threads(), + num_init_threads: DEFAULT_THREADS, }; let renderer = diff --git a/packages/blitz/src/viewport.rs b/packages/blitz/src/viewport.rs index 55095bcf4..7dfaab971 100644 --- a/packages/blitz/src/viewport.rs +++ b/packages/blitz/src/viewport.rs @@ -1,7 +1,5 @@ -use style::{ - media_queries::{Device, MediaType}, - servo::media_queries::FontMetricsProvider, -}; +use blitz_dom::document::DummyFontMetricsProvider; +use style::media_queries::{Device, MediaType}; #[derive(Default, Debug)] pub struct Viewport { @@ -14,22 +12,6 @@ pub struct Viewport { pub font_size: f32, } -// TODO: implement a proper font metrics provider -#[derive(Debug, Clone)] -struct DummyFontMetricsProvider; -impl FontMetricsProvider for DummyFontMetricsProvider { - fn query_font_metrics( - &self, - _vertical: bool, - _font: &style::properties::style_structs::Font, - _base_size: style::values::computed::CSSPixelLength, - _in_media_query: bool, - _retrieve_math_scales: bool, - ) -> style::font_metrics::FontMetrics { - Default::default() - } -} - impl Viewport { pub fn new(window_size: (u32, u32)) -> Self { Self { diff --git a/packages/dioxus-blitz/src/documents/dioxus_document.rs b/packages/dioxus-blitz/src/documents/dioxus_document.rs index 82655f28b..82fff2866 100644 --- a/packages/dioxus-blitz/src/documents/dioxus_document.rs +++ b/packages/dioxus-blitz/src/documents/dioxus_document.rs @@ -48,30 +48,29 @@ impl AsMut for DioxusDocument { &mut self.inner } } -impl Into for DioxusDocument { - fn into(self) -> Document { - self.inner +impl From for Document { + fn from(doc: DioxusDocument) -> Document { + doc.inner } } impl DocumentLike for DioxusDocument { fn poll(&mut self, mut cx: std::task::Context) -> bool { - loop { - { - let fut = self.vdom.wait_for_work(); - pin_mut!(fut); - - match fut.poll_unpin(&mut cx) { - std::task::Poll::Ready(_) => {} - std::task::Poll::Pending => return false, - } - } + { + let fut = self.vdom.wait_for_work(); + pin_mut!(fut); - self.vdom.render_immediate(&mut MutationWriter { - doc: &mut self.inner, - state: &mut self.vdom_state, - }); - return true; + match fut.poll_unpin(&mut cx) { + std::task::Poll::Ready(_) => {} + std::task::Poll::Pending => return false, + } } + + self.vdom.render_immediate(&mut MutationWriter { + doc: &mut self.inner, + state: &mut self.vdom_state, + }); + + true } fn handle_event(&mut self, event: blitz_dom::events::RendererEvent) -> bool { diff --git a/packages/dioxus-blitz/src/documents/html_document.rs b/packages/dioxus-blitz/src/documents/html_document.rs index 22922d1a9..aa520f6ff 100644 --- a/packages/dioxus-blitz/src/documents/html_document.rs +++ b/packages/dioxus-blitz/src/documents/html_document.rs @@ -19,9 +19,9 @@ impl AsMut for HtmlDocument { &mut self.inner } } -impl Into for HtmlDocument { - fn into(self) -> Document { - self.inner +impl From for Document { + fn from(doc: HtmlDocument) -> Document { + doc.inner } } impl DocumentLike for HtmlDocument {} diff --git a/packages/dioxus-blitz/src/window.rs b/packages/dioxus-blitz/src/window.rs index 5fb5c6269..920b5580d 100644 --- a/packages/dioxus-blitz/src/window.rs +++ b/packages/dioxus-blitz/src/window.rs @@ -296,7 +296,9 @@ impl<'a, Doc: DocumentLike> View<'a, Doc> { } #[cfg(target_os = "linux")] { - build_menu().init_for_gtk_window(window.gtk_window(), window.default_vbox()); + build_menu() + .init_for_gtk_window(window.gtk_window(), window.default_vbox()) + .unwrap(); } // !TODO - this may not be the right way to do this, but it's a start @@ -331,10 +333,10 @@ impl<'a, Doc: DocumentLike> View<'a, Doc> { #[cfg(not(target_os = "macos"))] fn build_menu() -> Menu { - let mut menu = Menu::new(); + let menu = Menu::new(); // Build the about section - let mut about = Submenu::new("About", true); + let about = Submenu::new("About", true); about .append_items(&[ diff --git a/packages/dom/src/document.rs b/packages/dom/src/document.rs index cfda135f7..7bbf9a8bb 100644 --- a/packages/dom/src/document.rs +++ b/packages/dom/src/document.rs @@ -7,6 +7,7 @@ use slab::Slab; use std::collections::HashMap; use style::invalidation::element::restyle_hints::RestyleHint; use style::selector_parser::ServoElementSnapshot; +use style::servo::media_queries::FontMetricsProvider; use style::servo_arc::Arc as ServoArc; use style::{ dom::{TDocument, TNode}, @@ -20,6 +21,22 @@ use style_traits::dom::ElementState; use taffy::AvailableSpace; use url::Url; +// TODO: implement a proper font metrics provider +#[derive(Debug, Clone)] +pub struct DummyFontMetricsProvider; +impl FontMetricsProvider for DummyFontMetricsProvider { + fn query_font_metrics( + &self, + _vertical: bool, + _font: &style::properties::style_structs::Font, + _base_size: style::values::computed::CSSPixelLength, + _in_media_query: bool, + _retrieve_math_scales: bool, + ) -> style::font_metrics::FontMetrics { + Default::default() + } +} + pub trait DocumentLike: AsRef + AsMut + Into { fn poll(&mut self, _cx: std::task::Context) -> bool { // Default implementation does nothing diff --git a/packages/dom/src/htmlsink.rs b/packages/dom/src/htmlsink.rs index 98e4fba20..03397648f 100644 --- a/packages/dom/src/htmlsink.rs +++ b/packages/dom/src/htmlsink.rs @@ -270,8 +270,7 @@ impl<'b> TreeSink for DocumentHtmlParser<'b> { if has_appended { return; } else { - let id = self.create_text_node(&text); - id + self.create_text_node(&text) } } NodeOrText::AppendNode(id) => id, @@ -366,6 +365,7 @@ impl<'b> TreeSink for DocumentHtmlParser<'b> { #[test] fn parses_some_html() { + use crate::document::DummyFontMetricsProvider; use euclid::{Scale, Size2D}; use style::media_queries::{Device, MediaType}; @@ -378,6 +378,7 @@ fn parses_some_html() { selectors::matching::QuirksMode::NoQuirks, viewport_size, device_pixel_ratio, + Box::new(DummyFontMetricsProvider), ); let mut doc = Document::new(device); let sink = DocumentHtmlParser::new(&mut doc); diff --git a/packages/dom/src/layout/mod.rs b/packages/dom/src/layout/mod.rs index 4916dd5a0..94e100f3d 100644 --- a/packages/dom/src/layout/mod.rs +++ b/packages/dom/src/layout/mod.rs @@ -411,13 +411,13 @@ impl PrintTree for Document { } } -pub struct ChildIter<'a>(std::slice::Iter<'a, usize>); -impl<'a> Iterator for ChildIter<'a> { - type Item = NodeId; - fn next(&mut self) -> Option { - self.0.next().copied().map(NodeId::from) - } -} +// pub struct ChildIter<'a>(std::slice::Iter<'a, usize>); +// impl<'a> Iterator for ChildIter<'a> { +// type Item = NodeId; +// fn next(&mut self) -> Option { +// self.0.next().copied().map(NodeId::from) +// } +// } pub struct RefCellChildIter<'a> { items: Ref<'a, [usize]>, diff --git a/packages/dom/src/stylo.rs b/packages/dom/src/stylo.rs index 0334f01d1..10e99f6ef 100644 --- a/packages/dom/src/stylo.rs +++ b/packages/dom/src/stylo.rs @@ -815,8 +815,7 @@ impl<'a> TElement for BlitzNode<'a> { let root_element = TDocument::as_node(&root_node) .first_element_child() .unwrap(); - let is_child_of_root_element = root_element.children.contains(&self.id); - is_child_of_root_element + root_element.children.contains(&self.id) } fn synthesize_presentational_hints_for_legacy_attributes( diff --git a/packages/dom/src/stylo_to_parley.rs b/packages/dom/src/stylo_to_parley.rs index f5a724db4..066eb0e42 100644 --- a/packages/dom/src/stylo_to_parley.rs +++ b/packages/dom/src/stylo_to_parley.rs @@ -72,7 +72,7 @@ pub(crate) fn style(style: &stylo::ComputedValues) -> parley::TextStyle<'static, } // TODO: fix leak! - parley::FontFamily::Named(name.to_string().leak()) + break 'ret parley::FontFamily::Named(name.to_string().leak()); } } stylo::SingleFontFamily::Generic(generic) => { diff --git a/packages/dom/src/util.rs b/packages/dom/src/util.rs index 03ff0f92c..bacd07c65 100644 --- a/packages/dom/src/util.rs +++ b/packages/dom/src/util.rs @@ -6,14 +6,17 @@ use image::DynamicImage; const USER_AGENT: &str = "Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/81.0"; const FILE_SIZE_LIMIT: u64 = 1_000_000_000; // 1GB -pub(crate) fn fetch_blob(url: &str) -> Result, ureq::Error> { +pub(crate) fn fetch_blob(url: &str) -> Result, Box> { if url.starts_with("data:") { let data_url = data_url::DataUrl::process(url).unwrap(); let decoded = data_url.decode_to_vec().expect("Invalid data url"); return Ok(decoded.0); } - let resp = ureq::get(url).set("User-Agent", USER_AGENT).call()?; + let resp = ureq::get(url) + .set("User-Agent", USER_AGENT) + .call() + .map_err(Box::new)?; let len: usize = resp .header("Content-Length") @@ -23,12 +26,13 @@ pub(crate) fn fetch_blob(url: &str) -> Result, ureq::Error> { resp.into_reader() .take(FILE_SIZE_LIMIT) - .read_to_end(&mut bytes)?; + .read_to_end(&mut bytes) + .unwrap(); Ok(bytes) } -pub(crate) fn fetch_string(url: &str) -> Result { +pub(crate) fn fetch_string(url: &str) -> Result> { fetch_blob(url).map(|vec| String::from_utf8(vec).expect("Invalid UTF8")) } @@ -41,11 +45,11 @@ pub(crate) fn fetch_string(url: &str) -> Result { #[allow(unused)] pub(crate) enum ImageFetchErr { - FetchErr(ureq::Error), + FetchErr(Box), ImageError(image::error::ImageError), } -impl From for ImageFetchErr { - fn from(value: ureq::Error) -> Self { +impl From> for ImageFetchErr { + fn from(value: Box) -> Self { Self::FetchErr(value) } } diff --git a/tests/stylo_usage.rs b/tests/stylo_usage.rs index 7cbcc3f40..5cecdb614 100644 --- a/tests/stylo_usage.rs +++ b/tests/stylo_usage.rs @@ -1,158 +1,157 @@ -pub use blitz::style_impls::{BlitzNode, RealDom}; -use dioxus::prelude::*; -use style::{ - animation::DocumentAnimationSet, - context::{QuirksMode, SharedStyleContext}, - driver, - global_style_data::GLOBAL_STYLE_DATA, - media_queries::MediaType, - media_queries::{Device as StyleDevice, MediaList}, - selector_parser::SnapshotMap, - servo_arc::Arc, - shared_lock::{SharedRwLock, StylesheetGuards}, - stylesheets::{AllowImportRules, DocumentStyleSheet, Origin, Stylesheet}, - stylist::Stylist, - thread_state::ThreadState, - traversal::DomTraversal, - traversal_flags::TraversalFlags, -}; - -fn main() { - let css = r#" - h1 { - background-color: red; - } - - h2 { - background-color: green; - } - - h3 { - background-color: blue; - } - - h4 { - background-color: yellow; - } - - "#; - - let nodes = rsx! { - h1 { } - h2 { } - h3 { } - h4 { } - }; - - let styled_dom = style_lazy_nodes(css, nodes); - - // print_styles(&styled_dom); -} - -pub fn style_lazy_nodes(css: &str, markup: LazyNodes) -> RealDom { - const QUIRKS_MODE: QuirksMode = QuirksMode::NoQuirks; - - // Figured out a single-pass system from the servo repo itself: - // - // components/layout_thread_2020/lib.rs:795 - // handle_reflow - // tests/unit/style/custom_properties.rs - style::thread_state::enter(ThreadState::LAYOUT); - - // make the guards that we use to thread everything together - let guard = SharedRwLock::new(); - let guards = StylesheetGuards { - author: &guard.read(), - ua_or_user: &guard.read(), - }; - - // Make some CSS - let stylesheet = Stylesheet::from_str( - css, - servo_url::ServoUrl::from_url("data:text/css;charset=utf-8;base64,".parse().unwrap()), - Origin::UserAgent, - Arc::new(guard.wrap(MediaList::empty())), - guard.clone(), - None, - None, - QUIRKS_MODE, - 0, - AllowImportRules::Yes, - ); - - // Make the real domtree by converting dioxus vnodes - let markup = RealDom::from_dioxus(markup); - - // Now we need to do what handle_reflow does in servo - // Reflows should be fast due to caching in the Stylist object - // - // - // We can force reflows. Happens in the script/document section - // The browser keeps track of pending restyles itself when attributes are changed. - // When something like val.set_attribute() happens, the pending reflow is inserted into the list. - // Once ticks are finished, the ScriptReflow object is created and sent to the layout thread. - // The layout thread uses the ScriptReflow object to inform itself on what changes need to happen. - // Zooming and touching causes full reflows. - // For this demo we want to do complete reflows (have yet to figure it out) - // But eventually we'll want to queue up modifications to the ECS engine and then build the script-reflow type object. - // Unfortunately, this API assumes nodes are backed by pointers which adds some unsafe where we wouldn't want it. - // - // Reflow allows us to specify a dirty root node and a list of nodes to reflow. - // - // Notes: - // - https://developers.google.com/speed/docs/insights/browser-reflow - // - components/script/dom/window.rs:force_reflow - // - // Create a styling context for use throughout the following passes. - // In servo we'd also create a layout context, but since servo isn't updated with the new layout code, we're just using the styling context - // In a different world we'd use both - // Build the stylist object from our screen requirements - // Todo: pull this in from wgpu - let mut stylist = Stylist::new( - StyleDevice::new( - MediaType::screen(), - QUIRKS_MODE, - euclid::Size2D::new(800., 600.), - euclid::Scale::new(1.0), - ), - QUIRKS_MODE, - ); - - // We have no snapshots on initial render, but we will need them for future renders - let snapshots = SnapshotMap::new(); - - // Add the stylesheets to the stylist - stylist.append_stylesheet(DocumentStyleSheet(Arc::new(stylesheet)), &guard.read()); - - // We don't really need to do this, but it's worth keeping it here anyways - stylist.force_stylesheet_origins_dirty(Origin::Author.into()); - - // Note that html5ever parses the first node as the document, so we need to unwrap it and get the first child - // For the sake of this demo, it's always just a single body node, but eventually we will want to construct something like the - // BoxTree struct that servo uses. - stylist.flush(&guards, Some(markup.root_element()), Some(&snapshots)); - - // Build the style context used by the style traversal - let context = SharedStyleContext { - traversal_flags: TraversalFlags::empty(), - stylist: &stylist, - options: GLOBAL_STYLE_DATA.options.clone(), - guards, - visited_styles_enabled: false, - animations: (&DocumentAnimationSet::default()).clone(), - current_time_for_animations: 0.0, - snapshot_map: &snapshots, - registered_speculative_painters: &style_impls::RegisteredPaintersImpl, - }; - - // components/layout_2020/lib.rs:983 - println!("------Pre-traversing the DOM tree -----"); - let token = style_traverser::RecalcStyle::pre_traverse(markup.root_element(), &context); - - // Style the elements, resolving their data - println!("------ Traversing domtree ------",); - let traverser = style_traverser::RecalcStyle::new(context); - driver::traverse_dom(&traverser, token, None); - - markup -} +// pub use blitz::style_impls::{BlitzNode, RealDom}; +// use dioxus::prelude::*; +// use style::{ +// animation::DocumentAnimationSet, +// context::{QuirksMode, SharedStyleContext}, +// driver, +// global_style_data::GLOBAL_STYLE_DATA, +// media_queries::MediaType, +// media_queries::{Device as StyleDevice, MediaList}, +// selector_parser::SnapshotMap, +// servo_arc::Arc, +// shared_lock::{SharedRwLock, StylesheetGuards}, +// stylesheets::{AllowImportRules, DocumentStyleSheet, Origin, Stylesheet}, +// stylist::Stylist, +// thread_state::ThreadState, +// traversal::DomTraversal, +// traversal_flags::TraversalFlags, +// }; + +// fn main() { +// let css = r#" +// h1 { +// background-color: red; +// } + +// h2 { +// background-color: green; +// } + +// h3 { +// background-color: blue; +// } + +// h4 { +// background-color: yellow; +// } + +// "#; + +// let nodes = rsx! { +// h1 { } +// h2 { } +// h3 { } +// h4 { } +// }; + +// let styled_dom = style_lazy_nodes(css, nodes); + +// // print_styles(&styled_dom); +// } + +// // pub fn style_lazy_nodes(css: &str, markup: LazyNodes) -> RealDom { +// // const QUIRKS_MODE: QuirksMode = QuirksMode::NoQuirks; + +// // // Figured out a single-pass system from the servo repo itself: +// // // +// // // components/layout_thread_2020/lib.rs:795 +// // // handle_reflow +// // // tests/unit/style/custom_properties.rs +// // style::thread_state::enter(ThreadState::LAYOUT); + +// // // make the guards that we use to thread everything together +// // let guard = SharedRwLock::new(); +// // let guards = StylesheetGuards { +// // author: &guard.read(), +// // ua_or_user: &guard.read(), +// // }; + +// // // Make some CSS +// // let stylesheet = Stylesheet::from_str( +// // css, +// // servo_url::ServoUrl::from_url("data:text/css;charset=utf-8;base64,".parse().unwrap()), +// // Origin::UserAgent, +// // Arc::new(guard.wrap(MediaList::empty())), +// // guard.clone(), +// // None, +// // None, +// // QUIRKS_MODE, +// // AllowImportRules::Yes, +// // ); + +// // // Make the real domtree by converting dioxus vnodes +// // let markup = RealDom::from_dioxus(markup); + +// // // Now we need to do what handle_reflow does in servo +// // // Reflows should be fast due to caching in the Stylist object +// // // +// // // +// // // We can force reflows. Happens in the script/document section +// // // The browser keeps track of pending restyles itself when attributes are changed. +// // // When something like val.set_attribute() happens, the pending reflow is inserted into the list. +// // // Once ticks are finished, the ScriptReflow object is created and sent to the layout thread. +// // // The layout thread uses the ScriptReflow object to inform itself on what changes need to happen. +// // // Zooming and touching causes full reflows. +// // // For this demo we want to do complete reflows (have yet to figure it out) +// // // But eventually we'll want to queue up modifications to the ECS engine and then build the script-reflow type object. +// // // Unfortunately, this API assumes nodes are backed by pointers which adds some unsafe where we wouldn't want it. +// // // +// // // Reflow allows us to specify a dirty root node and a list of nodes to reflow. +// // // +// // // Notes: +// // // - https://developers.google.com/speed/docs/insights/browser-reflow +// // // - components/script/dom/window.rs:force_reflow +// // // +// // // Create a styling context for use throughout the following passes. +// // // In servo we'd also create a layout context, but since servo isn't updated with the new layout code, we're just using the styling context +// // // In a different world we'd use both +// // // Build the stylist object from our screen requirements +// // // Todo: pull this in from wgpu +// // let mut stylist = Stylist::new( +// // StyleDevice::new( +// // MediaType::screen(), +// // QUIRKS_MODE, +// // euclid::Size2D::new(800., 600.), +// // euclid::Scale::new(1.0), +// // ), +// // QUIRKS_MODE, +// // ); + +// // // We have no snapshots on initial render, but we will need them for future renders +// // let snapshots = SnapshotMap::new(); + +// // // Add the stylesheets to the stylist +// // stylist.append_stylesheet(DocumentStyleSheet(Arc::new(stylesheet)), &guard.read()); + +// // // We don't really need to do this, but it's worth keeping it here anyways +// // stylist.force_stylesheet_origins_dirty(Origin::Author.into()); + +// // // Note that html5ever parses the first node as the document, so we need to unwrap it and get the first child +// // // For the sake of this demo, it's always just a single body node, but eventually we will want to construct something like the +// // // BoxTree struct that servo uses. +// // stylist.flush(&guards, Some(markup.root_element()), Some(&snapshots)); + +// // // Build the style context used by the style traversal +// // let context = SharedStyleContext { +// // traversal_flags: TraversalFlags::empty(), +// // stylist: &stylist, +// // options: GLOBAL_STYLE_DATA.options.clone(), +// // guards, +// // visited_styles_enabled: false, +// // animations: (&DocumentAnimationSet::default()).clone(), +// // current_time_for_animations: 0.0, +// // snapshot_map: &snapshots, +// // registered_speculative_painters: &style_impls::RegisteredPaintersImpl, +// // }; + +// // // components/layout_2020/lib.rs:983 +// // println!("------Pre-traversing the DOM tree -----"); +// // let token = style_traverser::RecalcStyle::pre_traverse(markup.root_element(), &context); + +// // // Style the elements, resolving their data +// // println!("------ Traversing domtree ------",); +// // let traverser = style_traverser::RecalcStyle::new(context); +// // driver::traverse_dom(&traverser, token, None); + +// // markup +// // } From bb6eb9d534471e36da6a0c6827710965d8e9c69c Mon Sep 17 00:00:00 2001 From: Matt Hunzinger Date: Wed, 19 Jun 2024 20:26:51 -0400 Subject: [PATCH 2/3] Replace tao with winit (#69) --- packages/dioxus-blitz/Cargo.toml | 2 +- packages/dioxus-blitz/src/lib.rs | 166 ++++++++++++++-------------- packages/dioxus-blitz/src/waker.rs | 2 +- packages/dioxus-blitz/src/window.rs | 150 +++++++++++++------------ 4 files changed, 164 insertions(+), 156 deletions(-) diff --git a/packages/dioxus-blitz/Cargo.toml b/packages/dioxus-blitz/Cargo.toml index a85095c4f..0ef49217b 100644 --- a/packages/dioxus-blitz/Cargo.toml +++ b/packages/dioxus-blitz/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -tao = { version = "0.26.1", features = ["serde"] } +winit = "0.30.2" muda = { version = "0.11.5", features = ["serde"] } tokio = { workspace = true, features = ["full"] } dioxus = { workspace = true } diff --git a/packages/dioxus-blitz/src/lib.rs b/packages/dioxus-blitz/src/lib.rs index 46e30b885..6c922c5c4 100644 --- a/packages/dioxus-blitz/src/lib.rs +++ b/packages/dioxus-blitz/src/lib.rs @@ -11,13 +11,13 @@ use dioxus::prelude::*; use documents::DioxusDocument; use muda::{MenuEvent, MenuId}; use std::collections::HashMap; -use tao::event_loop::EventLoopBuilder; -use tao::window::WindowId; -use tao::{ +use url::Url; +use winit::event_loop::{EventLoop, EventLoopBuilder}; +use winit::window::WindowId; +use winit::{ event::{Event, WindowEvent}, event_loop::ControlFlow, }; -use url::Url; #[derive(Default)] pub struct Config { @@ -93,7 +93,9 @@ fn launch_with_window(window: View<'static, Doc>) { let _guard = rt.enter(); // Build an event loop for the application - let event_loop = EventLoopBuilder::::with_user_event().build(); + let event_loop = EventLoop::::with_user_event() + .build() + .unwrap(); let proxy = event_loop.create_proxy(); // Multiwindow ftw @@ -106,94 +108,96 @@ fn launch_with_window(window: View<'static, Doc>) { #[cfg(not(any(target_os = "android", target_os = "ios")))] let mut initial = true; - event_loop.run(move |event, event_loop, control_flow| { - *control_flow = ControlFlow::Wait; + event_loop + .run(move |event, event_loop| { + event_loop.set_control_flow(ControlFlow::Wait); - let mut on_resume = || { - for (_, view) in windows.iter_mut() { - view.resume(event_loop, &proxy, &rt); - } + let mut on_resume = || { + for (_, view) in windows.iter_mut() { + view.resume(event_loop, &proxy, &rt); + } - for view in pending_windows.iter_mut() { - view.resume(event_loop, &proxy, &rt); - } + for view in pending_windows.iter_mut() { + view.resume(event_loop, &proxy, &rt); + } - for window in pending_windows.drain(..) { - let RenderState::Active(state) = &window.renderer.render_state else { - continue; - }; - windows.insert(state.window.id(), window); - } - }; - - #[cfg(not(any(target_os = "android", target_os = "ios")))] - if initial { - on_resume(); - initial = false; - } - - match event { - // Exit the app when close is request - // Not always necessary - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => *control_flow = ControlFlow::Exit, - - // Nothing else to do, try redrawing? - Event::MainEventsCleared => {} - - Event::UserEvent(UserWindowEvent(EventData::Poll, id)) => { - if let Some(view) = windows.get_mut(&id) { - if view.poll() { - view.request_redraw(); - } - }; - } - // Event::UserEvent(_redraw) => { - // for (_, view) in windows.iter() { - // view.request_redraw(); - // } - // } - Event::NewEvents(_) => { - for id in windows.keys() { - _ = proxy.send_event(UserWindowEvent(EventData::Poll, *id)); + for window in pending_windows.drain(..) { + let RenderState::Active(state) = &window.renderer.render_state else { + continue; + }; + windows.insert(state.window.id(), window); } - } + }; - Event::RedrawRequested(window_id) => { - if let Some(window) = windows.get_mut(&window_id) { - window.renderer.dom.as_mut().resolve(); - window.renderer.render(&mut window.scene); - }; + #[cfg(not(any(target_os = "android", target_os = "ios")))] + if initial { + on_resume(); + initial = false; } - Event::Suspended => { - for (_, view) in windows.iter_mut() { - view.suspend(); + match event { + // Exit the app when close is request + // Not always necessary + Event::WindowEvent { + event: WindowEvent::CloseRequested, + .. + } => event_loop.exit(), + + Event::WindowEvent { + window_id, + event: winit::event::WindowEvent::RedrawRequested, + } => { + if let Some(window) = windows.get_mut(&window_id) { + window.renderer.dom.as_mut().resolve(); + window.renderer.render(&mut window.scene); + }; } - } - Event::Resumed => on_resume(), + Event::UserEvent(UserWindowEvent(EventData::Poll, id)) => { + if let Some(view) = windows.get_mut(&id) { + if view.poll() { + view.request_redraw(); + } + }; + } + // Event::UserEvent(_redraw) => { + // for (_, view) in windows.iter() { + // view.request_redraw(); + // } + // } + Event::NewEvents(_) => { + for id in windows.keys() { + _ = proxy.send_event(UserWindowEvent(EventData::Poll, *id)); + } + } - Event::WindowEvent { - window_id, event, .. - } => { - if let Some(window) = windows.get_mut(&window_id) { - window.handle_window_event(event); - }; - } + Event::Suspended => { + for (_, view) in windows.iter_mut() { + view.suspend(); + } + } - _ => (), - } + Event::Resumed => on_resume(), - if let Ok(event) = menu_channel.try_recv() { - if event.id == MenuId::new("dev.show_layout") { - for (_, view) in windows.iter_mut() { - view.renderer.devtools.show_layout = !view.renderer.devtools.show_layout; - view.request_redraw(); + Event::WindowEvent { + window_id, event, .. + } => { + if let Some(window) = windows.get_mut(&window_id) { + window.handle_window_event(event); + }; } + + _ => (), } - } - }); + + if let Ok(event) = menu_channel.try_recv() { + if event.id == MenuId::new("dev.show_layout") { + for (_, view) in windows.iter_mut() { + view.renderer.devtools.show_layout = !view.renderer.devtools.show_layout; + view.request_redraw(); + } + } + } + }) + .unwrap(); } diff --git a/packages/dioxus-blitz/src/waker.rs b/packages/dioxus-blitz/src/waker.rs index 2b391ad4e..ff78fb26b 100644 --- a/packages/dioxus-blitz/src/waker.rs +++ b/packages/dioxus-blitz/src/waker.rs @@ -1,6 +1,6 @@ use futures_util::task::ArcWake; use std::sync::Arc; -use tao::{event_loop::EventLoopProxy, window::WindowId}; +use winit::{event_loop::EventLoopProxy, window::WindowId}; #[derive(Debug, Clone)] pub struct UserWindowEvent(pub EventData, pub WindowId); diff --git a/packages/dioxus-blitz/src/window.rs b/packages/dioxus-blitz/src/window.rs index 920b5580d..ad757219d 100644 --- a/packages/dioxus-blitz/src/window.rs +++ b/packages/dioxus-blitz/src/window.rs @@ -1,12 +1,16 @@ use crate::waker::UserWindowEvent; use blitz::{RenderState, Renderer, Viewport}; use blitz_dom::DocumentLike; +use wgpu::rwh::HasWindowHandle; +use winit::keyboard::PhysicalKey; +use winit::window::WindowAttributes; use std::sync::Arc; use std::task::Waker; -use tao::dpi::LogicalSize; -use tao::event::{ElementState, MouseButton}; -use tao::event_loop::{EventLoopProxy, EventLoopWindowTarget}; +use vello::Scene; +use winit::dpi::LogicalSize; +use winit::event::{ElementState, MouseButton}; +use winit::event_loop::{ActiveEventLoop, EventLoopProxy}; #[cfg(any( target_os = "linux", target_os = "dragonfly", @@ -14,16 +18,10 @@ use tao::event_loop::{EventLoopProxy, EventLoopWindowTarget}; target_os = "netbsd", target_os = "openbsd" ))] -use tao::platform::unix::WindowExtUnix; +use winit::platform::unix::WindowExtUnix; #[cfg(target_os = "windows")] -use tao::platform::windows::WindowExtWindows; -use tao::{ - event::WindowEvent, - keyboard::KeyCode, - keyboard::ModifiersState, - window::{Window, WindowBuilder}, -}; -use vello::Scene; +use winit::platform::windows::WindowExtWindows; +use winit::{event::WindowEvent, keyboard::KeyCode, keyboard::ModifiersState, window::Window}; #[cfg(not(target_os = "macos"))] use muda::{AboutMetadata, Menu, MenuId, MenuItem, PredefinedMenuItem, Submenu}; @@ -91,7 +89,7 @@ impl<'a, Doc: DocumentLike> View<'a, Doc> { // Store new keyboard modifier (ctrl, shift, etc) state for later use WindowEvent::ModifiersChanged(new_state) => { - self.keyboard_modifiers = new_state; + self.keyboard_modifiers = new_state.state(); } // todo: if there's an active text input, we want to direct input towards it and translate system emi text @@ -99,54 +97,60 @@ impl<'a, Doc: DocumentLike> View<'a, Doc> { dbg!(&event); match event.physical_key { - KeyCode::Equal => { - if self.keyboard_modifiers.control_key() - || self.keyboard_modifiers.super_key() - { - self.renderer.zoom(0.1); - self.request_redraw(); - } - } - KeyCode::Minus => { - if self.keyboard_modifiers.control_key() - || self.keyboard_modifiers.super_key() - { - self.renderer.zoom(-0.1); - self.request_redraw(); - } - } - KeyCode::Digit0 => { - if self.keyboard_modifiers.control_key() - || self.keyboard_modifiers.super_key() - { - self.renderer.reset_zoom(); - self.request_redraw(); - } - } - KeyCode::KeyD => { - if event.state == ElementState::Pressed && self.keyboard_modifiers.alt_key() - { - self.renderer.devtools.show_layout = - !self.renderer.devtools.show_layout; - self.request_redraw(); - } - } - KeyCode::KeyH => { - if event.state == ElementState::Pressed && self.keyboard_modifiers.alt_key() - { - self.renderer.devtools.highlight_hover = - !self.renderer.devtools.highlight_hover; - self.request_redraw(); + PhysicalKey::Code(key_code) => { + match key_code { + KeyCode::Equal => { + if self.keyboard_modifiers.control_key() + || self.keyboard_modifiers.super_key() + { + self.renderer.zoom(0.1); + self.request_redraw(); + } + } + KeyCode::Minus => { + if self.keyboard_modifiers.control_key() + || self.keyboard_modifiers.super_key() + { + self.renderer.zoom(-0.1); + self.request_redraw(); + } + } + KeyCode::Digit0 => { + if self.keyboard_modifiers.control_key() + || self.keyboard_modifiers.super_key() + { + self.renderer.reset_zoom(); + self.request_redraw(); + } + } + KeyCode::KeyD => { + if event.state == ElementState::Pressed && self.keyboard_modifiers.alt_key() + { + self.renderer.devtools.show_layout = + !self.renderer.devtools.show_layout; + self.request_redraw(); + } + } + KeyCode::KeyH => { + if event.state == ElementState::Pressed && self.keyboard_modifiers.alt_key() + { + self.renderer.devtools.highlight_hover = + !self.renderer.devtools.highlight_hover; + self.request_redraw(); + } + } + KeyCode::KeyT => { + if event.state == ElementState::Pressed && self.keyboard_modifiers.alt_key() + { + self.renderer.print_taffy_tree(); + } + } + _ => {} } - } - KeyCode::KeyT => { - if event.state == ElementState::Pressed && self.keyboard_modifiers.alt_key() - { - self.renderer.print_taffy_tree(); - } - } - _ => {} + }, + PhysicalKey::Unidentified(_) => {} } + } WindowEvent::Moved(_) => {} WindowEvent::CloseRequested => {} @@ -154,7 +158,6 @@ impl<'a, Doc: DocumentLike> View<'a, Doc> { WindowEvent::DroppedFile(_) => {} WindowEvent::HoveredFile(_) => {} WindowEvent::HoveredFileCancelled => {} - WindowEvent::ReceivedImeText(_) => {} WindowEvent::Focused(_) => {} WindowEvent::CursorMoved { // device_id, @@ -163,7 +166,7 @@ impl<'a, Doc: DocumentLike> View<'a, Doc> { .. } => { let changed = if let RenderState::Active(state) = &self.renderer.render_state { - let tao::dpi::LogicalPosition:: { x, y } = position.to_logical(state.window.scale_factor()); + let winit::dpi::LogicalPosition:: { x, y } = position.to_logical(state.window.scale_factor()); self.renderer.mouse_move(x, y) } else { @@ -175,11 +178,11 @@ impl<'a, Doc: DocumentLike> View<'a, Doc> { if let Some(cursor) = cursor { use style::values::computed::ui::CursorKind; - use tao::window::CursorIcon as TaoCursor; + use winit::window::CursorIcon as TaoCursor; let tao_cursor = match cursor { CursorKind::None => todo!("set the cursor to none"), CursorKind::Default => TaoCursor::Default, - CursorKind::Pointer => TaoCursor::Hand, + CursorKind::Pointer => TaoCursor::Pointer, CursorKind::ContextMenu => TaoCursor::ContextMenu, CursorKind::Help => TaoCursor::Help, CursorKind::Progress => TaoCursor::Progress, @@ -240,10 +243,10 @@ impl<'a, Doc: DocumentLike> View<'a, Doc> { .. } => { match delta { - tao::event::MouseScrollDelta::LineDelta(_, y) => { + winit::event::MouseScrollDelta::LineDelta(_, y) => { self.renderer.scroll_by(y as f64 * 20.0) } - tao::event::MouseScrollDelta::PixelDelta(offsets) => { + winit::event::MouseScrollDelta::PixelDelta(offsets) => { self.renderer.scroll_by(offsets.y) } _ => {} @@ -270,29 +273,30 @@ impl<'a, Doc: DocumentLike> View<'a, Doc> { .. } => {} WindowEvent::ThemeChanged(_) => {} - WindowEvent::DecorationsClick => {} _ => {} } } pub fn resume( &mut self, - event_loop: &EventLoopWindowTarget, + event_loop: &ActiveEventLoop, proxy: &EventLoopProxy, rt: &tokio::runtime::Runtime, ) { let window_builder = || { - let window = WindowBuilder::new() - .with_inner_size(LogicalSize { + let window = event_loop + .create_window(Window::default_attributes().with_inner_size(LogicalSize { width: 800, height: 600, - }) - .build(event_loop) + })) .unwrap(); #[cfg(target_os = "windows")] { - build_menu().init_for_hwnd(window.hwnd()); + use winit::raw_window_handle::*; + if let RawWindowHandle::Win32(handle) = window.window_handle().unwrap().as_raw() { + build_menu().init_for_hwnd(handle.hwnd.get()).unwrap(); + } } #[cfg(target_os = "linux")] { @@ -308,7 +312,7 @@ impl<'a, Doc: DocumentLike> View<'a, Doc> { // build_menu().set_as_windows_menu_for_nsapp(); // } - let size: tao::dpi::PhysicalSize = window.inner_size(); + let size: winit::dpi::PhysicalSize = window.inner_size(); let mut viewport = Viewport::new((size.width, size.height)); viewport.set_hidpi_scale(window.scale_factor() as _); From 2e99ca125db4fbfd50738dd30a29c9b61bcd8693 Mon Sep 17 00:00:00 2001 From: Nico Burns Date: Thu, 20 Jun 2024 13:06:53 +1200 Subject: [PATCH 3/3] Remove unused dependencies (#73) * cargo fmt * Remove unused dependencies --- packages/blitz/Cargo.toml | 8 -------- packages/dioxus-blitz/Cargo.toml | 1 - packages/dioxus-blitz/src/window.rs | 1 - packages/dom/Cargo.toml | 3 --- 4 files changed, 13 deletions(-) diff --git a/packages/blitz/Cargo.toml b/packages/blitz/Cargo.toml index abfab1ef5..1d01a462c 100644 --- a/packages/blitz/Cargo.toml +++ b/packages/blitz/Cargo.toml @@ -15,10 +15,6 @@ parley = { workspace = true } tokio = { workspace = true, features = ["full"] } vello = { workspace = true } wgpu = { workspace = true } -shipyard = { version = "0.6.2", features = [ - "proc", - "std", -], default-features = false } app_units = "0.7.3" atomic_refcell = { version = "0.1.13", features = ["serde"] } @@ -28,10 +24,6 @@ string_cache = "0.8.7" futures-util = "0.3.29" raw-window-handle = "0.6.0" blitz-dom = { path = "../dom" } -glyphon = "0.5.0" -cosmic-text = "0.11.2" -quadtree_rs = "0.1.3" -askama_escape = "0.10.3" image = "0.25" # futures-util = "0.3.29" diff --git a/packages/dioxus-blitz/Cargo.toml b/packages/dioxus-blitz/Cargo.toml index 0ef49217b..93702b22d 100644 --- a/packages/dioxus-blitz/Cargo.toml +++ b/packages/dioxus-blitz/Cargo.toml @@ -10,7 +10,6 @@ winit = "0.30.2" muda = { version = "0.11.5", features = ["serde"] } tokio = { workspace = true, features = ["full"] } dioxus = { workspace = true } -dioxus-ssr = { workspace = true } futures-util = "0.3.30" vello = { workspace = true } wgpu = { workspace = true } diff --git a/packages/dioxus-blitz/src/window.rs b/packages/dioxus-blitz/src/window.rs index ad757219d..3ee7ab0ac 100644 --- a/packages/dioxus-blitz/src/window.rs +++ b/packages/dioxus-blitz/src/window.rs @@ -150,7 +150,6 @@ impl<'a, Doc: DocumentLike> View<'a, Doc> { }, PhysicalKey::Unidentified(_) => {} } - } WindowEvent::Moved(_) => {} WindowEvent::CloseRequested => {} diff --git a/packages/dom/Cargo.toml b/packages/dom/Cargo.toml index 42d131431..f55512f76 100644 --- a/packages/dom/Cargo.toml +++ b/packages/dom/Cargo.toml @@ -20,14 +20,11 @@ atomic_refcell = { version = "0.1.13", features = ["serde"] } fxhash = "0.2.1" html5ever = { workspace = true } string_cache = "0.8.7" -futures-util = "0.3.30" -askama_escape = "0.10.3" html-escape = "0.2.13" url = { version = "2.5.0", features = ["serde"] } data-url = "0.3.1" ureq = "2.9" image = "0.25" -quadtree_rs = "0.1.3" # on wasm use the js feature on getrandom