diff --git a/packages/core/README.md b/packages/core/README.md index f02ec114c..45059cd89 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -40,3 +40,19 @@ We have big goals for Dioxus. The final implementation must: - Be "live". Components should be able to be both server rendered and client rendered without needing frontend APIs. - Be modular. Components and hooks should be work anywhere without worrying about target platform. + + +## Safety + +Dioxus deals with arenas, lifetimes, asynchronous tasks, custom allocators, pinning, and a lot more foundational low-level work that is very difficult to implement with 0 unsafe. + +If you don't want to use a crate that uses unsafe, then this crate is not for you. + +however, we are always interested in decreasing the scope of the core VirtualDom to make it easier to review. + +We'd also be happy to welcome PRs that can eliminate unsafe code while still upholding the numerous variants required to execute certain features. + +There's a few invariants that are very important: + +- References to `ScopeInner` and `Props` passed into components are *always* valid for as long as the component exists. Even if the scope backing is resized to fit more scopes, the scope has to stay the same place in memory. + diff --git a/packages/core/src/diff.rs b/packages/core/src/diff.rs index f59d89490..f22e0d617 100644 --- a/packages/core/src/diff.rs +++ b/packages/core/src/diff.rs @@ -105,7 +105,6 @@ use DomEdit::*; /// Funnily enough, this stack machine's entire job is to create instructions for another stack machine to execute. It's /// stack machines all the way down! pub(crate) struct DiffMachine<'bump> { - pub vdom: &'bump ResourcePool, pub mutations: Mutations<'bump>, pub stack: DiffStack<'bump>, pub seen_scopes: FxHashSet, @@ -137,10 +136,9 @@ impl<'a> SavedDiffWork<'a> { std::mem::transmute(self) } - pub unsafe fn promote<'b>(self, vdom: &'b ResourcePool) -> DiffMachine<'b> { + pub unsafe fn promote<'b>(self) -> DiffMachine<'b> { let extended: SavedDiffWork<'b> = std::mem::transmute(self); DiffMachine { - vdom, cfg: DiffCfg::default(), mutations: extended.mutations, stack: extended.stack, @@ -150,12 +148,11 @@ impl<'a> SavedDiffWork<'a> { } impl<'bump> DiffMachine<'bump> { - pub(crate) fn new(mutations: Mutations<'bump>, shared: &'bump ResourcePool) -> Self { + pub(crate) fn new(mutations: Mutations<'bump>) -> Self { Self { mutations, cfg: DiffCfg::default(), stack: DiffStack::new(), - vdom: shared, seen_scopes: FxHashSet::default(), } } diff --git a/packages/core/src/events.rs b/packages/core/src/events.rs index 23d415562..70635aa2e 100644 --- a/packages/core/src/events.rs +++ b/packages/core/src/events.rs @@ -14,8 +14,6 @@ use std::{ fmt::Debug, }; -pub use on::*; - #[derive(Debug)] pub struct UserEvent { /// The originator of the event trigger @@ -84,1126 +82,3 @@ pub enum EventPriority { /// This is considered "idle" work or "background" work. Low = 0, } - -pub mod on { - use super::*; - macro_rules! event_directory { - ( $( - $( #[$attr:meta] )* - $wrapper:ident: [ - // $eventdata:ident($wrapper:ident): [ - $( - $( #[$method_attr:meta] )* - $name:ident - )* - ]; - )* ) => { - $( - $( - $(#[$method_attr])* - pub fn $name<'a, F>( - c: NodeFactory<'a>, - mut callback: F, - ) -> Listener<'a> - where F: FnMut($wrapper) + 'a - { - let bump = &c.bump(); - - // we can't allocate unsized in bumpalo's box, so we need to craft the box manually - // safety: this is essentially the same as calling Box::new() but manually - // The box is attached to the lifetime of the bumpalo allocator - let cb: &mut dyn FnMut(Box) = bump.alloc(move |evt: Box| { - let event = evt.downcast::<$wrapper>().unwrap(); - callback(*event) - }); - - let callback: BumpBox) + 'a> = unsafe { BumpBox::from_raw(cb) }; - - // ie oncopy - let event_name = stringify!($name); - - // ie copy - let shortname: &'static str = &event_name[2..]; - - Listener { - event: shortname, - mounted_node: Cell::new(None), - callback: RefCell::new(Some(callback)), - } - } - )* - )* - }; - } - - // The Dioxus Synthetic event system - // todo: move these into the html event system. dioxus accepts *any* event, so having these here doesn't make sense. - event_directory! { - ClipboardEvent: [ - /// Called when "copy" - oncopy - - /// oncut - oncut - - /// onpaste - onpaste - ]; - - CompositionEvent: [ - /// oncompositionend - oncompositionend - - /// oncompositionstart - oncompositionstart - - /// oncompositionupdate - oncompositionupdate - ]; - - KeyboardEvent: [ - /// onkeydown - onkeydown - - /// onkeypress - onkeypress - - /// onkeyup - onkeyup - ]; - - FocusEvent: [ - /// onfocus - onfocus - - /// onblur - onblur - ]; - - FormEvent: [ - /// onchange - onchange - - /// oninput handler - oninput - - /// oninvalid - oninvalid - - /// onreset - onreset - - /// onsubmit - onsubmit - ]; - - /// A synthetic event that wraps a web-style [`MouseEvent`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent) - /// - /// - /// The MouseEvent interface represents events that occur due to the user interacting with a pointing device (such as a mouse). - /// - /// ## Trait implementation: - /// ```rust - /// fn alt_key(&self) -> bool; - /// fn button(&self) -> i16; - /// fn buttons(&self) -> u16; - /// fn client_x(&self) -> i32; - /// fn client_y(&self) -> i32; - /// fn ctrl_key(&self) -> bool; - /// fn meta_key(&self) -> bool; - /// fn page_x(&self) -> i32; - /// fn page_y(&self) -> i32; - /// fn screen_x(&self) -> i32; - /// fn screen_y(&self) -> i32; - /// fn shift_key(&self) -> bool; - /// fn get_modifier_state(&self, key_code: &str) -> bool; - /// ``` - /// - /// ## Event Handlers - /// - [`onclick`] - /// - [`oncontextmenu`] - /// - [`ondoubleclick`] - /// - [`ondrag`] - /// - [`ondragend`] - /// - [`ondragenter`] - /// - [`ondragexit`] - /// - [`ondragleave`] - /// - [`ondragover`] - /// - [`ondragstart`] - /// - [`ondrop`] - /// - [`onmousedown`] - /// - [`onmouseenter`] - /// - [`onmouseleave`] - /// - [`onmousemove`] - /// - [`onmouseout`] - /// - [`onmouseover`] - /// - [`onmouseup`] - MouseEvent: [ - /// Execute a callback when a button is clicked. - /// - /// ## Description - /// - /// An element receives a click event when a pointing device button (such as a mouse's primary mouse button) - /// is both pressed and released while the pointer is located inside the element. - /// - /// - Bubbles: Yes - /// - Cancelable: Yes - /// - Interface: [`MouseEvent`] - /// - /// If the button is pressed on one element and the pointer is moved outside the element before the button - /// is released, the event is fired on the most specific ancestor element that contained both elements. - /// `click` fires after both the `mousedown` and `mouseup` events have fired, in that order. - /// - /// ## Example - /// ``` - /// rsx!( button { "click me", onclick: move |_| log::info!("Clicked!`") } ) - /// ``` - /// - /// ## Reference - /// - https://www.w3schools.com/tags/ev_onclick.asp - /// - https://developer.mozilla.org/en-US/docs/Web/API/Element/click_event - onclick - - /// oncontextmenu - oncontextmenu - - /// ondoubleclick - ondoubleclick - - /// ondrag - ondrag - - /// ondragend - ondragend - - /// ondragenter - ondragenter - - /// ondragexit - ondragexit - - /// ondragleave - ondragleave - - /// ondragover - ondragover - - /// ondragstart - ondragstart - - /// ondrop - ondrop - - /// onmousedown - onmousedown - - /// onmouseenter - onmouseenter - - /// onmouseleave - onmouseleave - - /// onmousemove - onmousemove - - /// onmouseout - onmouseout - - /// - onscroll - - /// onmouseover - /// - /// Triggered when the users's mouse hovers over an element. - onmouseover - - /// onmouseup - onmouseup - ]; - - PointerEvent: [ - /// pointerdown - onpointerdown - - /// pointermove - onpointermove - - /// pointerup - onpointerup - - /// pointercancel - onpointercancel - - /// gotpointercapture - ongotpointercapture - - /// lostpointercapture - onlostpointercapture - - /// pointerenter - onpointerenter - - /// pointerleave - onpointerleave - - /// pointerover - onpointerover - - /// pointerout - onpointerout - ]; - - SelectionEvent: [ - /// onselect - onselect - ]; - - TouchEvent: [ - /// ontouchcancel - ontouchcancel - - /// ontouchend - ontouchend - - /// ontouchmove - ontouchmove - - /// ontouchstart - ontouchstart - ]; - - WheelEvent: [ - /// - onwheel - ]; - - MediaEvent: [ - ///abort - onabort - - ///canplay - oncanplay - - ///canplaythrough - oncanplaythrough - - ///durationchange - ondurationchange - - ///emptied - onemptied - - ///encrypted - onencrypted - - ///ended - onended - - ///error - onerror - - ///loadeddata - onloadeddata - - ///loadedmetadata - onloadedmetadata - - ///loadstart - onloadstart - - ///pause - onpause - - ///play - onplay - - ///playing - onplaying - - ///progress - onprogress - - ///ratechange - onratechange - - ///seeked - onseeked - - ///seeking - onseeking - - ///stalled - onstalled - - ///suspend - onsuspend - - ///timeupdate - ontimeupdate - - ///volumechange - onvolumechange - - ///waiting - onwaiting - ]; - - AnimationEvent: [ - /// onanimationstart - onanimationstart - - /// onanimationend - onanimationend - - /// onanimationiteration - onanimationiteration - ]; - - TransitionEvent: [ - /// - ontransitionend - ]; - - ToggleEvent: [ - /// - ontoggle - ]; - } - - #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] - #[derive(Debug)] - pub struct ClipboardEvent( - // DOMDataTransfer clipboardData - ); - - #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] - #[derive(Debug)] - pub struct CompositionEvent { - pub data: String, - } - - #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] - #[derive(Debug)] - pub struct KeyboardEvent { - pub char_code: u32, - - /// Identify which "key" was entered. - /// - /// This is the best method to use for all languages. They key gets mapped to a String sequence which you can match on. - /// The key isn't an enum because there are just so many context-dependent keys. - /// - /// A full list on which keys to use is available at: - /// - /// - /// # Example - /// - /// ```rust - /// match event.key().as_str() { - /// "Esc" | "Escape" => {} - /// "ArrowDown" => {} - /// "ArrowLeft" => {} - /// _ => {} - /// } - /// ``` - /// - pub key: String, - - /// Get the key code as an enum Variant. - /// - /// This is intended for things like arrow keys, escape keys, function keys, and other non-international keys. - /// To match on unicode sequences, use the [`key`] method - this will return a string identifier instead of a limited enum. - /// - /// - /// ## Example - /// - /// ```rust - /// use dioxus::KeyCode; - /// match event.key_code() { - /// KeyCode::Escape => {} - /// KeyCode::LeftArrow => {} - /// KeyCode::RightArrow => {} - /// _ => {} - /// } - /// ``` - /// - pub key_code: KeyCode, - - /// Indicate if the `alt` modifier key was pressed during this keyboard event - pub alt_key: bool, - - /// Indicate if the `ctrl` modifier key was pressed during this keyboard event - pub ctrl_key: bool, - - /// Indicate if the `meta` modifier key was pressed during this keyboard event - pub meta_key: bool, - - /// Indicate if the `shift` modifier key was pressed during this keyboard event - pub shift_key: bool, - - pub locale: String, - - pub location: usize, - - pub repeat: bool, - - pub which: usize, - // get_modifier_state: bool, - } - - #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] - #[derive(Debug)] - pub struct FocusEvent {/* DOMEventInner: Send + SyncTarget relatedTarget */} - - #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] - #[derive(Debug)] - pub struct FormEvent { - pub value: String, - /* DOMEvent: Send + SyncTarget relatedTarget */ - } - - #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] - #[derive(Debug)] - pub struct MouseEvent { - pub alt_key: bool, - pub button: i16, - pub buttons: u16, - pub client_x: i32, - pub client_y: i32, - pub ctrl_key: bool, - pub meta_key: bool, - pub page_x: i32, - pub page_y: i32, - pub screen_x: i32, - pub screen_y: i32, - pub shift_key: bool, - // fn get_modifier_state(&self, key_code: &str) -> bool; - } - - #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] - #[derive(Debug)] - pub struct PointerEvent { - // Mouse only - pub alt_key: bool, - pub button: i16, - pub buttons: u16, - pub client_x: i32, - pub client_y: i32, - pub ctrl_key: bool, - pub meta_key: bool, - pub page_x: i32, - pub page_y: i32, - pub screen_x: i32, - pub screen_y: i32, - pub shift_key: bool, - pub pointer_id: i32, - pub width: i32, - pub height: i32, - pub pressure: f32, - pub tangential_pressure: f32, - pub tilt_x: i32, - pub tilt_y: i32, - pub twist: i32, - pub pointer_type: String, - pub is_primary: bool, - // pub get_modifier_state: bool, - } - - #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] - #[derive(Debug)] - pub struct SelectionEvent {} - - #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] - #[derive(Debug)] - pub struct TouchEvent { - pub alt_key: bool, - pub ctrl_key: bool, - pub meta_key: bool, - pub shift_key: bool, - // get_modifier_state: bool, - // changedTouches: DOMTouchList, - // targetTouches: DOMTouchList, - // touches: DOMTouchList, - } - - #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] - #[derive(Debug)] - pub struct WheelEvent { - pub delta_mode: u32, - pub delta_x: f64, - pub delta_y: f64, - pub delta_z: f64, - } - - #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] - #[derive(Debug)] - pub struct MediaEvent {} - - #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] - #[derive(Debug)] - pub struct ImageEvent { - pub load_error: bool, - } - - #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] - #[derive(Debug)] - pub struct AnimationEvent { - pub animation_name: String, - pub pseudo_element: String, - pub elapsed_time: f32, - } - - #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] - #[derive(Debug)] - pub struct TransitionEvent { - pub property_name: String, - pub pseudo_element: String, - pub elapsed_time: f32, - } - - #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] - #[derive(Debug)] - pub struct ToggleEvent {} -} - -#[cfg_attr( - feature = "serialize", - derive(serde_repr::Serialize_repr, serde_repr::Deserialize_repr) -)] -#[derive(Clone, Copy, Debug)] -#[repr(u8)] -pub enum KeyCode { - // That key has no keycode, = 0 - // break, = 3 - // backspace / delete, = 8 - // tab, = 9 - // clear, = 12 - // enter, = 13 - // shift, = 16 - // ctrl, = 17 - // alt, = 18 - // pause/break, = 19 - // caps lock, = 20 - // hangul, = 21 - // hanja, = 25 - // escape, = 27 - // conversion, = 28 - // non-conversion, = 29 - // spacebar, = 32 - // page up, = 33 - // page down, = 34 - // end, = 35 - // home, = 36 - // left arrow, = 37 - // up arrow, = 38 - // right arrow, = 39 - // down arrow, = 40 - // select, = 41 - // print, = 42 - // execute, = 43 - // Print Screen, = 44 - // insert, = 45 - // delete, = 46 - // help, = 47 - // 0, = 48 - // 1, = 49 - // 2, = 50 - // 3, = 51 - // 4, = 52 - // 5, = 53 - // 6, = 54 - // 7, = 55 - // 8, = 56 - // 9, = 57 - // :, = 58 - // semicolon (firefox), equals, = 59 - // <, = 60 - // equals (firefox), = 61 - // ß, = 63 - // @ (firefox), = 64 - // a, = 65 - // b, = 66 - // c, = 67 - // d, = 68 - // e, = 69 - // f, = 70 - // g, = 71 - // h, = 72 - // i, = 73 - // j, = 74 - // k, = 75 - // l, = 76 - // m, = 77 - // n, = 78 - // o, = 79 - // p, = 80 - // q, = 81 - // r, = 82 - // s, = 83 - // t, = 84 - // u, = 85 - // v, = 86 - // w, = 87 - // x, = 88 - // y, = 89 - // z, = 90 - // Windows Key / Left ⌘ / Chromebook Search key, = 91 - // right window key, = 92 - // Windows Menu / Right ⌘, = 93 - // sleep, = 95 - // numpad 0, = 96 - // numpad 1, = 97 - // numpad 2, = 98 - // numpad 3, = 99 - // numpad 4, = 100 - // numpad 5, = 101 - // numpad 6, = 102 - // numpad 7, = 103 - // numpad 8, = 104 - // numpad 9, = 105 - // multiply, = 106 - // add, = 107 - // numpad period (firefox), = 108 - // subtract, = 109 - // decimal point, = 110 - // divide, = 111 - // f1, = 112 - // f2, = 113 - // f3, = 114 - // f4, = 115 - // f5, = 116 - // f6, = 117 - // f7, = 118 - // f8, = 119 - // f9, = 120 - // f10, = 121 - // f11, = 122 - // f12, = 123 - // f13, = 124 - // f14, = 125 - // f15, = 126 - // f16, = 127 - // f17, = 128 - // f18, = 129 - // f19, = 130 - // f20, = 131 - // f21, = 132 - // f22, = 133 - // f23, = 134 - // f24, = 135 - // f25, = 136 - // f26, = 137 - // f27, = 138 - // f28, = 139 - // f29, = 140 - // f30, = 141 - // f31, = 142 - // f32, = 143 - // num lock, = 144 - // scroll lock, = 145 - // airplane mode, = 151 - // ^, = 160 - // !, = 161 - // ؛ (arabic semicolon), = 162 - // #, = 163 - // $, = 164 - // ù, = 165 - // page backward, = 166 - // page forward, = 167 - // refresh, = 168 - // closing paren (AZERTY), = 169 - // *, = 170 - // ~ + * key, = 171 - // home key, = 172 - // minus (firefox), mute/unmute, = 173 - // decrease volume level, = 174 - // increase volume level, = 175 - // next, = 176 - // previous, = 177 - // stop, = 178 - // play/pause, = 179 - // e-mail, = 180 - // mute/unmute (firefox), = 181 - // decrease volume level (firefox), = 182 - // increase volume level (firefox), = 183 - // semi-colon / ñ, = 186 - // equal sign, = 187 - // comma, = 188 - // dash, = 189 - // period, = 190 - // forward slash / ç, = 191 - // grave accent / ñ / æ / ö, = 192 - // ?, / or °, = 193 - // numpad period (chrome), = 194 - // open bracket, = 219 - // back slash, = 220 - // close bracket / å, = 221 - // single quote / ø / ä, = 222 - // `, = 223 - // left or right ⌘ key (firefox), = 224 - // altgr, = 225 - // < /git >, left back slash, = 226 - // GNOME Compose Key, = 230 - // ç, = 231 - // XF86Forward, = 233 - // XF86Back, = 234 - // non-conversion, = 235 - // alphanumeric, = 240 - // hiragana/katakana, = 242 - // half-width/full-width, = 243 - // kanji, = 244 - // unlock trackpad (Chrome/Edge), = 251 - // toggle touchpad, = 255 - NA = 0, - Break = 3, - Backspace = 8, - Tab = 9, - Clear = 12, - Enter = 13, - Shift = 16, - Ctrl = 17, - Alt = 18, - Pause = 19, - CapsLock = 20, - // hangul, = 21 - // hanja, = 25 - Escape = 27, - // conversion, = 28 - // non-conversion, = 29 - Space = 32, - PageUp = 33, - PageDown = 34, - End = 35, - Home = 36, - LeftArrow = 37, - UpArrow = 38, - RightArrow = 39, - DownArrow = 40, - // select, = 41 - // print, = 42 - // execute, = 43 - // Print Screen, = 44 - Insert = 45, - Delete = 46, - // help, = 47 - Num0 = 48, - Num1 = 49, - Num2 = 50, - Num3 = 51, - Num4 = 52, - Num5 = 53, - Num6 = 54, - Num7 = 55, - Num8 = 56, - Num9 = 57, - // :, = 58 - // semicolon (firefox), equals, = 59 - // <, = 60 - // equals (firefox), = 61 - // ß, = 63 - // @ (firefox), = 64 - A = 65, - B = 66, - C = 67, - D = 68, - E = 69, - F = 70, - G = 71, - H = 72, - I = 73, - J = 74, - K = 75, - L = 76, - M = 77, - N = 78, - O = 79, - P = 80, - Q = 81, - R = 82, - S = 83, - T = 84, - U = 85, - V = 86, - W = 87, - X = 88, - Y = 89, - Z = 90, - LeftWindow = 91, - RightWindow = 92, - SelectKey = 93, - Numpad0 = 96, - Numpad1 = 97, - Numpad2 = 98, - Numpad3 = 99, - Numpad4 = 100, - Numpad5 = 101, - Numpad6 = 102, - Numpad7 = 103, - Numpad8 = 104, - Numpad9 = 105, - Multiply = 106, - Add = 107, - Subtract = 109, - DecimalPoint = 110, - Divide = 111, - F1 = 112, - F2 = 113, - F3 = 114, - F4 = 115, - F5 = 116, - F6 = 117, - F7 = 118, - F8 = 119, - F9 = 120, - F10 = 121, - F11 = 122, - F12 = 123, - // f13, = 124 - // f14, = 125 - // f15, = 126 - // f16, = 127 - // f17, = 128 - // f18, = 129 - // f19, = 130 - // f20, = 131 - // f21, = 132 - // f22, = 133 - // f23, = 134 - // f24, = 135 - // f25, = 136 - // f26, = 137 - // f27, = 138 - // f28, = 139 - // f29, = 140 - // f30, = 141 - // f31, = 142 - // f32, = 143 - NumLock = 144, - ScrollLock = 145, - // airplane mode, = 151 - // ^, = 160 - // !, = 161 - // ؛ (arabic semicolon), = 162 - // #, = 163 - // $, = 164 - // ù, = 165 - // page backward, = 166 - // page forward, = 167 - // refresh, = 168 - // closing paren (AZERTY), = 169 - // *, = 170 - // ~ + * key, = 171 - // home key, = 172 - // minus (firefox), mute/unmute, = 173 - // decrease volume level, = 174 - // increase volume level, = 175 - // next, = 176 - // previous, = 177 - // stop, = 178 - // play/pause, = 179 - // e-mail, = 180 - // mute/unmute (firefox), = 181 - // decrease volume level (firefox), = 182 - // increase volume level (firefox), = 183 - Semicolon = 186, - EqualSign = 187, - Comma = 188, - Dash = 189, - Period = 190, - ForwardSlash = 191, - GraveAccent = 192, - // ?, / or °, = 193 - // numpad period (chrome), = 194 - OpenBracket = 219, - BackSlash = 220, - CloseBraket = 221, - SingleQuote = 222, - // `, = 223 - // left or right ⌘ key (firefox), = 224 - // altgr, = 225 - // < /git >, left back slash, = 226 - // GNOME Compose Key, = 230 - // ç, = 231 - // XF86Forward, = 233 - // XF86Back, = 234 - // non-conversion, = 235 - // alphanumeric, = 240 - // hiragana/katakana, = 242 - // half-width/full-width, = 243 - // kanji, = 244 - // unlock trackpad (Chrome/Edge), = 251 - // toggle touchpad, = 255 - #[cfg_attr(feature = "serialize", serde(other))] - Unknown, -} - -impl KeyCode { - pub fn from_raw_code(i: u8) -> Self { - use KeyCode::*; - match i { - 8 => Backspace, - 9 => Tab, - 13 => Enter, - 16 => Shift, - 17 => Ctrl, - 18 => Alt, - 19 => Pause, - 20 => CapsLock, - 27 => Escape, - 33 => PageUp, - 34 => PageDown, - 35 => End, - 36 => Home, - 37 => LeftArrow, - 38 => UpArrow, - 39 => RightArrow, - 40 => DownArrow, - 45 => Insert, - 46 => Delete, - 48 => Num0, - 49 => Num1, - 50 => Num2, - 51 => Num3, - 52 => Num4, - 53 => Num5, - 54 => Num6, - 55 => Num7, - 56 => Num8, - 57 => Num9, - 65 => A, - 66 => B, - 67 => C, - 68 => D, - 69 => E, - 70 => F, - 71 => G, - 72 => H, - 73 => I, - 74 => J, - 75 => K, - 76 => L, - 77 => M, - 78 => N, - 79 => O, - 80 => P, - 81 => Q, - 82 => R, - 83 => S, - 84 => T, - 85 => U, - 86 => V, - 87 => W, - 88 => X, - 89 => Y, - 90 => Z, - 91 => LeftWindow, - 92 => RightWindow, - 93 => SelectKey, - 96 => Numpad0, - 97 => Numpad1, - 98 => Numpad2, - 99 => Numpad3, - 100 => Numpad4, - 101 => Numpad5, - 102 => Numpad6, - 103 => Numpad7, - 104 => Numpad8, - 105 => Numpad9, - 106 => Multiply, - 107 => Add, - 109 => Subtract, - 110 => DecimalPoint, - 111 => Divide, - 112 => F1, - 113 => F2, - 114 => F3, - 115 => F4, - 116 => F5, - 117 => F6, - 118 => F7, - 119 => F8, - 120 => F9, - 121 => F10, - 122 => F11, - 123 => F12, - 144 => NumLock, - 145 => ScrollLock, - 186 => Semicolon, - 187 => EqualSign, - 188 => Comma, - 189 => Dash, - 190 => Period, - 191 => ForwardSlash, - 192 => GraveAccent, - 219 => OpenBracket, - 220 => BackSlash, - 221 => CloseBraket, - 222 => SingleQuote, - _ => Unknown, - } - } - - // get the raw code - pub fn raw_code(&self) -> u32 { - *self as u32 - } -} - -pub(crate) fn event_meta(event: &UserEvent) -> (bool, EventPriority) { - use EventPriority::*; - - match event.name { - // clipboard - "copy" | "cut" | "paste" => (true, Medium), - - // Composition - "compositionend" | "compositionstart" | "compositionupdate" => (true, Low), - - // Keyboard - "keydown" | "keypress" | "keyup" => (true, High), - - // Focus - "focus" | "blur" => (true, Low), - - // Form - "change" | "input" | "invalid" | "reset" | "submit" => (true, Medium), - - // Mouse - "click" | "contextmenu" | "doubleclick" | "drag" | "dragend" | "dragenter" | "dragexit" - | "dragleave" | "dragover" | "dragstart" | "drop" | "mousedown" | "mouseenter" - | "mouseleave" | "mouseout" | "mouseover" | "mouseup" => (true, High), - - "mousemove" => (false, Medium), - - // Pointer - "pointerdown" | "pointermove" | "pointerup" | "pointercancel" | "gotpointercapture" - | "lostpointercapture" | "pointerenter" | "pointerleave" | "pointerover" | "pointerout" => { - (true, Medium) - } - - // Selection - "select" | "touchcancel" | "touchend" => (true, Medium), - - // Touch - "touchmove" | "touchstart" => (true, Medium), - - // Wheel - "scroll" | "wheel" => (false, Medium), - - // Media - "abort" | "canplay" | "canplaythrough" | "durationchange" | "emptied" | "encrypted" - | "ended" | "error" | "loadeddata" | "loadedmetadata" | "loadstart" | "pause" | "play" - | "playing" | "progress" | "ratechange" | "seeked" | "seeking" | "stalled" | "suspend" - | "timeupdate" | "volumechange" | "waiting" => (true, Medium), - - // Animation - "animationstart" | "animationend" | "animationiteration" => (true, Medium), - - // Transition - "transitionend" => (true, Medium), - - // Toggle - "toggle" => (true, Medium), - - _ => (true, Low), - } -} diff --git a/packages/core/src/lib.rs b/packages/core/src/lib.rs index d63b155ed..322f0d48a 100644 --- a/packages/core/src/lib.rs +++ b/packages/core/src/lib.rs @@ -25,7 +25,6 @@ pub(crate) mod nodes; pub(crate) mod scope; pub(crate) mod scopearena; pub(crate) mod test_dom; -pub(crate) mod threadsafe; pub(crate) mod util; pub(crate) mod virtual_dom; @@ -46,7 +45,6 @@ pub(crate) mod innerlude { pub use crate::scope::*; pub use crate::scopearena::*; pub use crate::test_dom::*; - pub use crate::threadsafe::*; pub use crate::util::*; pub use crate::virtual_dom::*; @@ -56,8 +54,8 @@ pub(crate) mod innerlude { pub use crate::innerlude::{ Context, DioxusElement, DomEdit, Element, ElementId, EventPriority, LazyNodes, MountType, - Mutations, NodeFactory, Properties, ScopeChildren, ScopeId, TestDom, ThreadsafeVirtualDom, - UserEvent, VNode, VirtualDom, FC, + Mutations, NodeFactory, Properties, ScopeChildren, ScopeId, TestDom, UserEvent, VNode, + VirtualDom, FC, }; pub mod prelude { diff --git a/packages/core/src/childiter.rs b/packages/core/src/old/childiter.rs similarity index 100% rename from packages/core/src/childiter.rs rename to packages/core/src/old/childiter.rs diff --git a/packages/core/src/scheduler.rs b/packages/core/src/old/scheduler.rs similarity index 100% rename from packages/core/src/scheduler.rs rename to packages/core/src/old/scheduler.rs diff --git a/packages/core/src/threadsafe.rs b/packages/core/src/old/threadsafe.rs similarity index 100% rename from packages/core/src/threadsafe.rs rename to packages/core/src/old/threadsafe.rs diff --git a/packages/core/src/scope.rs b/packages/core/src/scope.rs index 026a53e65..477032566 100644 --- a/packages/core/src/scope.rs +++ b/packages/core/src/scope.rs @@ -180,58 +180,6 @@ impl ScopeInner { } } - /// Render this component. - /// - /// Returns true if the scope completed successfully and false if running failed (IE a None error was propagated). - pub(crate) fn run_scope<'sel>(&'sel mut self) -> bool { - // Cycle to the next frame and then reset it - // This breaks any latent references, invalidating every pointer referencing into it. - // Remove all the outdated listeners - self.ensure_drop_safety(); - - // Safety: - // - We dropped the listeners, so no more &mut T can be used while these are held - // - All children nodes that rely on &mut T are replaced with a new reference - unsafe { self.hooks.reset() }; - - // Safety: - // - We've dropped all references to the wip bump frame - unsafe { self.frames.reset_wip_frame() }; - - let items = self.items.get_mut(); - - // just forget about our suspended nodes while we're at it - items.suspended_nodes.clear(); - - // guarantee that we haven't screwed up - there should be no latent references anywhere - debug_assert!(items.listeners.is_empty()); - debug_assert!(items.suspended_nodes.is_empty()); - debug_assert!(items.borrowed_props.is_empty()); - - log::debug!("Borrowed stuff is successfully cleared"); - - // temporarily cast the vcomponent to the right lifetime - let vcomp = self.load_vcomp(); - - let render: &dyn for<'b> Fn(&'b ScopeInner) -> Element<'b> = todo!(); - - // Todo: see if we can add stronger guarantees around internal bookkeeping and failed component renders. - if let Some(builder) = render(self) { - let new_head = builder.into_vnode(NodeFactory { - bump: &self.frames.wip_frame().bump, - }); - log::debug!("Render is successful"); - - // the user's component succeeded. We can safely cycle to the next frame - self.frames.wip_frame_mut().head_node = unsafe { std::mem::transmute(new_head) }; - self.frames.cycle_frame(); - - true - } else { - false - } - } - pub(crate) fn new_subtree(&self) -> Option { todo!() // if self.is_subtree_root.get() { diff --git a/packages/core/src/scopearena.rs b/packages/core/src/scopearena.rs index e202723c7..715e7ae3a 100644 --- a/packages/core/src/scopearena.rs +++ b/packages/core/src/scopearena.rs @@ -5,6 +5,14 @@ use futures_channel::mpsc::UnboundedSender; use crate::innerlude::*; +pub type FcSlot = *const (); +// pub heuristics: FxHashMap, + +pub struct Heuristic { + hook_arena_size: usize, + node_arena_size: usize, +} + // a slab-like arena with stable references even when new scopes are allocated // uses a bump arena as a backing // @@ -24,6 +32,10 @@ impl ScopeArena { } } + pub fn get_mut(&mut self, id: &ScopeId) -> Option<&mut ScopeInner> { + unsafe { Some(&mut *self.scopes[id.0]) } + } + pub fn new_with_key( &mut self, fc_ptr: *const (), diff --git a/packages/core/src/test_dom.rs b/packages/core/src/test_dom.rs index 206276efc..63a0e2cbb 100644 --- a/packages/core/src/test_dom.rs +++ b/packages/core/src/test_dom.rs @@ -33,7 +33,7 @@ impl TestDom { pub fn diff<'a>(&'a self, old: &'a VNode<'a>, new: &'a VNode<'a>) -> Mutations<'a> { let mutations = Mutations::new(); - let mut machine = DiffMachine::new(mutations, todo!()); + let mut machine = DiffMachine::new(mutations); machine.stack.push(DiffInstruction::Diff { new, old }); machine.mutations } @@ -41,7 +41,7 @@ impl TestDom { pub fn create<'a>(&'a self, left: Option>) -> Mutations<'a> { let old = self.bump.alloc(self.render_direct(left)); - let mut machine = DiffMachine::new(Mutations::new(), todo!()); + let mut machine = DiffMachine::new(Mutations::new()); machine.stack.create_node(old, MountType::Append); @@ -57,14 +57,14 @@ impl TestDom { ) -> (Mutations<'a>, Mutations<'a>) { let (old, new) = (self.render(left), self.render(right)); - let mut machine = DiffMachine::new(Mutations::new(), todo!()); + let mut machine = DiffMachine::new(Mutations::new()); machine.stack.create_node(old, MountType::Append); machine.work(|| false); let create_edits = machine.mutations; - let mut machine = DiffMachine::new(Mutations::new(), todo!()); + let mut machine = DiffMachine::new(Mutations::new()); machine.stack.push(DiffInstruction::Diff { old, new }); diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index 239474334..73102a37e 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -80,12 +80,8 @@ pub struct VirtualDom { pub scopes: ScopeArena, - pub heuristics: FxHashMap, - pub receiver: UnboundedReceiver, - - // Garbage stored - pub pending_garbage: FxHashSet, + pub sender: UnboundedSender, // Every component that has futures that need to be polled pub pending_futures: FxHashSet, @@ -94,10 +90,6 @@ pub struct VirtualDom { pub pending_immediates: VecDeque, - pub batched_events: VecDeque, - - pub garbage_scopes: HashSet, - pub dirty_scopes: IndexSet, pub saved_state: Option>, @@ -173,7 +165,7 @@ impl VirtualDom { Self::new_with_props_and_scheduler(root, root_props, sender, receiver) } - /// Launch the VirtualDom, but provide your own channel for receiving and sending messages into the + /// Launch the VirtualDom, but provide your own channel for receiving and sending messages into the scheduler /// /// This is useful when the VirtualDom must be driven from outside a thread and it doesn't make sense to wait for the /// VirtualDom to be created just to retrieve its channel receiver. @@ -188,74 +180,36 @@ impl VirtualDom { let base_scope = scopes.new_with_key( // root as _, - boxed_comp.as_ref(), + todo!(), + // boxed_comp.as_ref(), None, 0, 0, sender.clone(), ); - // let root_fc = Box::new(root); - - // let root_props: Rc = Rc::new(root_props); - - // let props = root_props.clone(); - - // let mut root_caller: Box Element> = - // Box::new(move |scope: &ScopeInner| { - // let props = props.downcast_ref::

().unwrap(); - // let node = root((scope, props)); - // // cast into the right lifetime - // unsafe { std::mem::transmute(node) } - // }); - - // let caller = unsafe { bumpalo::boxed::Box::from_raw(root_caller.as_mut() as *mut _) }; - - // // todo make the memory footprint congifurable - // let scheduler = Scheduler::new(sender, receiver, 100, 2000); - - // let vcomp = VComponent { - // key: todo!(), - // associated_scope: todo!(), - // user_fc: root as *const _, - // can_memoize: todo!(), - // raw_props: todo!(), - // // drop_props: todo!(), - // // caller, - // comparator: todo!(), - // caller: todo!(), - // }; - - // let boxed_comp = Box::new(vcomp); - - // let base_scope = pool.insert_scope_with_key(|myidx| { - // ScopeInner::new( - // boxed_comp.as_ref(), - // myidx, - // None, - // 0, - // 0, - // pool.channel.clone(), - // ) - // }); - Self { scopes, - base_scope: todo!(), + base_scope, + receiver, + sender, + root_fc: todo!(), root_props: todo!(), _root_caller: todo!(), - heuristics: todo!(), - receiver, - pending_garbage: todo!(), - pending_futures: todo!(), + ui_events: todo!(), pending_immediates: todo!(), - batched_events: todo!(), - garbage_scopes: todo!(), - dirty_scopes: todo!(), - saved_state: todo!(), - in_progress: todo!(), + + pending_futures: Default::default(), + dirty_scopes: Default::default(), + + saved_state: Some(SavedDiffWork { + mutations: Mutations::new(), + stack: DiffStack::new(), + seen_scopes: Default::default(), + }), + in_progress: false, } } @@ -264,12 +218,14 @@ impl VirtualDom { /// This is useful for traversing the tree from the root for heuristics or alternsative renderers that use Dioxus /// directly. pub fn base_scope(&self) -> &ScopeInner { - self.pool.get_scope(&self.base_scope).unwrap() + todo!() + // self.get_scope(&self.base_scope).unwrap() } /// Get the [`Scope`] for a component given its [`ScopeId`] - pub fn get_scope(&self, id: ScopeId) -> Option<&ScopeInner> { - self.pool.get_scope(&id) + pub fn get_scope(&self, id: &ScopeId) -> Option<&ScopeInner> { + todo!() + // self.get_scope(&id) } /// Update the root props of this VirtualDOM. @@ -297,7 +253,7 @@ impl VirtualDom { let root_scope = self.pool.get_scope_mut(&self.base_scope).unwrap(); // Pre-emptively drop any downstream references of the old props - root_scope.ensure_drop_safety(&self.pool); + root_scope.ensure_drop_safety(); let mut root_props: Rc = Rc::new(root_props); @@ -339,6 +295,7 @@ impl VirtualDom { /// apply_edits(edits); /// ``` pub fn rebuild(&mut self) -> Mutations { + todo!() // self.rebuild(self.base_scope) } @@ -440,7 +397,7 @@ impl VirtualDom { } pub fn get_event_sender(&self) -> futures_channel::mpsc::UnboundedSender { - self.pool.channel.sender.clone() + self.sender.clone() } /// Waits for the scheduler to have work @@ -481,13 +438,6 @@ impl VirtualDom { } } -pub type FcSlot = *const (); - -pub struct Heuristic { - hook_arena_size: usize, - node_arena_size: usize, -} - /* Welcome to Dioxus's cooperative, priority-based scheduler. @@ -670,19 +620,15 @@ impl VirtualDom { let saved_state = unsafe { self.load_work() }; // We have to split away some parts of ourself - current lane is borrowed mutably - let shared = self.clone(); - let mut machine = unsafe { saved_state.promote(&shared) }; + let mut machine = unsafe { saved_state.promote() }; let mut ran_scopes = FxHashSet::default(); if machine.stack.is_empty() { - let shared = self.clone(); - - self.dirty_scopes - .retain(|id| shared.get_scope(id).is_some()); + self.dirty_scopes.retain(|id| self.get_scope(id).is_some()); self.dirty_scopes.sort_by(|a, b| { - let h1 = shared.get_scope(a).unwrap().height; - let h2 = shared.get_scope(b).unwrap().height; + let h1 = self.get_scope(a).unwrap().height; + let h2 = self.get_scope(b).unwrap().height; h1.cmp(&h2).reverse() }); @@ -692,15 +638,14 @@ impl VirtualDom { ran_scopes.insert(scopeid); log::debug!("about to run scope {:?}", scopeid); - if let Some(component) = self.get_scope_mut(&scopeid) { - if component.run_scope(&self) { - let (old, new) = - (component.frames.wip_head(), component.frames.fin_head()); - // let (old, new) = (component.frames.wip_head(), component.frames.fin_head()); - machine.stack.scope_stack.push(scopeid); - machine.stack.push(DiffInstruction::Diff { new, old }); - } + // if let Some(component) = self.get_scope_mut(&scopeid) { + if self.run_scope(&scopeid) { + let (old, new) = (component.frames.wip_head(), component.frames.fin_head()); + // let (old, new) = (component.frames.wip_head(), component.frames.fin_head()); + machine.stack.scope_stack.push(scopeid); + machine.stack.push(DiffInstruction::Diff { new, old }); } + // } } } } @@ -856,19 +801,17 @@ impl VirtualDom { /// /// Typically used to kickstart the VirtualDOM after initialization. pub fn rebuild_inner(&mut self, base_scope: ScopeId) -> Mutations { - let mut shared = self.clone(); - let mut diff_machine = DiffMachine::new(Mutations::new(), &mut shared); + let mut diff_machine = DiffMachine::new(Mutations::new()); // TODO: drain any in-flight work let cur_component = self - .pool .get_scope_mut(&base_scope) .expect("The base scope should never be moved"); log::debug!("rebuild {:?}", base_scope); // We run the component. If it succeeds, then we can diff it and add the changes to the dom. - if cur_component.run_scope(&self) { + if self.run_scope(&base_scope) { diff_machine .stack .create_node(cur_component.frames.fin_head(), MountType::Append); @@ -889,14 +832,13 @@ impl VirtualDom { pub fn hard_diff(&mut self, base_scope: ScopeId) -> Mutations { let cur_component = self - .pool .get_scope_mut(&base_scope) .expect("The base scope should never be moved"); log::debug!("hard diff {:?}", base_scope); - if cur_component.run_scope(&self) { - let mut diff_machine = DiffMachine::new(Mutations::new(), &mut self); + if self.run_scope(&base_scope) { + let mut diff_machine = DiffMachine::new(Mutations::new()); diff_machine.cfg.force_diff = true; diff_machine.diff_scope(base_scope); diff_machine.mutations @@ -904,36 +846,93 @@ impl VirtualDom { Mutations::new() } } -} -impl Future for VirtualDom { - type Output = (); + pub fn get_scope_mut(&mut self, id: &ScopeId) -> Option<&mut ScopeInner> { + self.scopes.get_mut(id) + } - fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll { - let mut all_pending = true; + pub fn run_scope(&mut self, id: &ScopeId) -> bool { + let scope = self + .get_scope_mut(id) + .expect("The base scope should never be moved"); - for fut in self.pending_futures.iter() { - let scope = self - .pool - .get_scope_mut(&fut) - .expect("Scope should never be moved"); + // Cycle to the next frame and then reset it + // This breaks any latent references, invalidating every pointer referencing into it. + // Remove all the outdated listeners + scope.ensure_drop_safety(); - let items = scope.items.get_mut(); - for task in items.tasks.iter_mut() { - let t = task.as_mut(); - let g = unsafe { Pin::new_unchecked(t) }; - match g.poll(cx) { - Poll::Ready(r) => { - all_pending = false; - } - Poll::Pending => {} - } - } - } + // Safety: + // - We dropped the listeners, so no more &mut T can be used while these are held + // - All children nodes that rely on &mut T are replaced with a new reference + unsafe { scope.hooks.reset() }; - match all_pending { - true => Poll::Pending, - false => Poll::Ready(()), + // Safety: + // - We've dropped all references to the wip bump frame + unsafe { scope.frames.reset_wip_frame() }; + + let items = scope.items.get_mut(); + + // just forget about our suspended nodes while we're at it + items.suspended_nodes.clear(); + + // guarantee that we haven't screwed up - there should be no latent references anywhere + debug_assert!(items.listeners.is_empty()); + debug_assert!(items.suspended_nodes.is_empty()); + debug_assert!(items.borrowed_props.is_empty()); + + log::debug!("Borrowed stuff is successfully cleared"); + + // temporarily cast the vcomponent to the right lifetime + let vcomp = scope.load_vcomp(); + + let render: &dyn for<'b> Fn(&'b ScopeInner) -> Element<'b> = todo!(); + + // Todo: see if we can add stronger guarantees around internal bookkeeping and failed component renders. + if let Some(builder) = render(scope) { + let new_head = builder.into_vnode(NodeFactory { + bump: &scope.frames.wip_frame().bump, + }); + log::debug!("Render is successful"); + + // the user's component succeeded. We can safely cycle to the next frame + scope.frames.wip_frame_mut().head_node = unsafe { std::mem::transmute(new_head) }; + scope.frames.cycle_frame(); + + true + } else { + false } } } + +// impl<'a> Future for PollAllTasks<'a> { +// type Output = (); + +// fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll { +// let mut all_pending = true; + +// for fut in self.pending_futures.iter() { +// let scope = self +// .pool +// .get_scope_mut(&fut) +// .expect("Scope should never be moved"); + +// let items = scope.items.get_mut(); +// for task in items.tasks.iter_mut() { +// let t = task.as_mut(); +// let g = unsafe { Pin::new_unchecked(t) }; +// match g.poll(cx) { +// Poll::Ready(r) => { +// all_pending = false; +// } +// Poll::Pending => {} +// } +// } +// } + +// match all_pending { +// true => Poll::Pending, +// false => Poll::Ready(()), +// } +// } +// } diff --git a/packages/html/Cargo.toml b/packages/html/Cargo.toml index 20ef160d4..b5d7e4993 100644 --- a/packages/html/Cargo.toml +++ b/packages/html/Cargo.toml @@ -9,7 +9,4 @@ description = "HTML Element pack for Dioxus - a concurrent renderer-agnostic Vir # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -dioxus-core = { path = "../core", version = "0.1.2" } - -[dev-dependencies] -scraper = "0.12.0" +dioxus-core = { path = "../core", version = "0.1.3" } diff --git a/packages/html/src/attrval.rs b/packages/html/src/attrval.rs deleted file mode 100644 index 9ac1f1373..000000000 --- a/packages/html/src/attrval.rs +++ /dev/null @@ -1,33 +0,0 @@ -//! This module is not included anywhere. -//! -//! It is a prototype for a system that supports non-string attribute values. - -trait AsAttributeValue: Sized { - fn into_attribute_value<'a>(self, cx: NodeFactory<'a>) -> AttributeValue<'a>; -} -enum AttributeValue<'a> { - Int(i32), - Float(f32), - Str(&'a str), - Bool(bool), -} -impl<'b> AsAttributeValue for Arguments<'b> { - fn into_attribute_value<'a>(self, cx: NodeFactory<'a>) -> AttributeValue<'a> { - todo!() - } -} -impl AsAttributeValue for &'static str { - fn into_attribute_value<'a>(self, cx: NodeFactory<'a>) -> AttributeValue<'a> { - todo!() - } -} -impl AsAttributeValue for f32 { - fn into_attribute_value<'a>(self, cx: NodeFactory<'a>) -> AttributeValue<'a> { - todo!() - } -} -impl AsAttributeValue for i32 { - fn into_attribute_value<'a>(self, cx: NodeFactory<'a>) -> AttributeValue<'a> { - todo!() - } -} diff --git a/packages/html/src/elements.rs b/packages/html/src/elements.rs new file mode 100644 index 000000000..f27f11ba1 --- /dev/null +++ b/packages/html/src/elements.rs @@ -0,0 +1,1122 @@ +macro_rules! builder_constructors { + ( + $( + $(#[$attr:meta])* + $name:ident { + $( + $(#[$attr_method:meta])* + $fil:ident: $vil:ident, + )* + }; + )* + ) => { + $( + #[allow(non_camel_case_types)] + $(#[$attr])* + pub struct $name; + + impl DioxusElement for $name { + const TAG_NAME: &'static str = stringify!($name); + const NAME_SPACE: Option<&'static str> = None; + } + + impl GlobalAttributes for $name {} + + impl $name { + $( + $(#[$attr_method])* + pub fn $fil<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> { + cx.attr(stringify!($fil), val, None, false) + } + )* + } + )* + }; + + ( $( + $(#[$attr:meta])* + $name:ident <> $namespace:tt { + $($fil:ident: $vil:ident,)* + }; + )* ) => { + $( + #[allow(non_camel_case_types)] + $(#[$attr])* + pub struct $name; + + impl DioxusElement for $name { + const TAG_NAME: &'static str = stringify!($name); + const NAME_SPACE: Option<&'static str> = Some($namespace); + } + + impl SvgAttributes for $name {} + + impl $name { + $( + pub fn $fil<'a>(&self, cx: NodeFactory<'a>, val: Arguments) -> Attribute<'a> { + cx.attr(stringify!($fil), val, Some(stringify!($namespace)), false) + } + )* + } + )* + }; +} + +// Organized in the same order as +// https://developer.mozilla.org/en-US/docs/Web/HTML/Element +// +// Does not include obsolete elements. +// +// This namespace represents a collection of modern HTML-5 compatiable elements. +// +// This list does not include obsolete, deprecated, experimental, or poorly supported elements. +builder_constructors! { + // Document metadata + + /// Build a + /// [``](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base) + /// element. + /// + base { + href: Uri, + target: Target, + }; + + /// Build a + /// [``](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/head) + /// element. + head {}; + + /// Build a + /// [``](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link) + /// element. + link { + // as: Mime, + crossorigin: CrossOrigin, + href: Uri, + hreflang: LanguageTag, + media: String, // FIXME media query + rel: LinkType, + sizes: String, // FIXME + title: String, // FIXME + r#type: Mime, + integrity: String, + }; + + /// Build a + /// [``](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta) + /// element. + meta { + charset: String, // FIXME IANA standard names + content: String, + http_equiv: HTTPEquiv, + name: Metadata, + }; + + /// Build a + /// [`