diff --git a/packages/core/src/diff.rs b/packages/core/src/diff.rs index 9dd315b6c..f12fd8bbf 100644 --- a/packages/core/src/diff.rs +++ b/packages/core/src/diff.rs @@ -42,8 +42,10 @@ impl<'b> VirtualDom { pub fn diff_scope(&mut self, mutations: &mut Mutations<'b>, scope: ScopeId) { let scope_state = &mut self.scopes[scope.0]; - let cur_arena = scope_state.current_frame(); - let prev_arena = scope_state.previous_frame(); + let cur_arena = scope_state.previous_frame(); + let prev_arena = scope_state.current_frame(); + // let cur_arena = scope_state.current_frame(); + // let prev_arena = scope_state.previous_frame(); // relax the borrow checker let cur_arena: &BumpFrame = unsafe { std::mem::transmute(cur_arena) }; @@ -62,8 +64,10 @@ impl<'b> VirtualDom { ); self.scope_stack.push(scope); + let left = unsafe { prev_arena.load_node() }; let right = unsafe { cur_arena.load_node() }; + self.diff_maybe_node(mutations, left, right); self.scope_stack.pop(); } @@ -105,6 +109,8 @@ impl<'b> VirtualDom { left_template: &'b VNode<'b>, right_template: &'b VNode<'b>, ) { + println!("diffing {:?} and {:?}", left_template, right_template); + if left_template.template.id != right_template.template.id { // do a light diff of the roots nodes. return; @@ -125,6 +131,7 @@ impl<'b> VirtualDom { .set(left_attr.mounted_element.get()); if left_attr.value != right_attr.value { + println!("DIFF ATTR: {:?} -> {:?}", left_attr, right_attr); let value = "todo!()"; muts.push(Mutation::SetAttribute { id: left_attr.mounted_element.get(), diff --git a/packages/core/src/scheduler/suspense.rs b/packages/core/src/scheduler/suspense.rs index ca0fcd3d0..8e252364f 100644 --- a/packages/core/src/scheduler/suspense.rs +++ b/packages/core/src/scheduler/suspense.rs @@ -19,6 +19,19 @@ pub struct SuspenseBoundary { pub waiting_on: RefCell>, pub mutations: RefCell>, pub placeholder: Cell>, + + // whenever the suspense resolves, we call this onresolve function + // this lets us do things like putting up a loading spinner + // + // todo: we need a way of controlling whether or not a component hides itself but still processes changes + // If we run into suspense, we perform a diff, so its important that the old elements are still around. + // + // When the timer expires, I imagine a container could hide the elements and show the spinner. This, however, + // can not be + pub onresolve: Option>, + + /// Called when + pub onstart: Option>, } impl SuspenseBoundary { @@ -28,6 +41,8 @@ impl SuspenseBoundary { waiting_on: Default::default(), mutations: RefCell::new(Mutations::new(0)), placeholder: Cell::new(None), + onresolve: None, + onstart: None, }) } } diff --git a/packages/core/src/scheduler/task.rs b/packages/core/src/scheduler/task.rs index f71431a84..5ff831216 100644 --- a/packages/core/src/scheduler/task.rs +++ b/packages/core/src/scheduler/task.rs @@ -1,8 +1,9 @@ use super::{waker::RcWake, Scheduler, SchedulerMsg}; use crate::ScopeId; +use std::cell::RefCell; use std::future::Future; use std::task::Context; -use std::{cell::UnsafeCell, pin::Pin, rc::Rc, task::Poll}; +use std::{pin::Pin, rc::Rc, task::Poll}; #[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] @@ -10,23 +11,19 @@ pub struct TaskId(pub usize); /// the task itself is the waker pub(crate) struct LocalTask { - pub id: TaskId, pub scope: ScopeId, - pub tx: futures_channel::mpsc::UnboundedSender, - - // todo: use rc and weak, or the bump slab instead of unsafecell - pub task: UnsafeCell + 'static>>>, + id: TaskId, + tx: futures_channel::mpsc::UnboundedSender, + task: RefCell + 'static>>>, } impl LocalTask { - pub fn progress(self: &Rc) -> bool { + /// Poll this task and return whether or not it is complete + pub(super) fn progress(self: &Rc) -> bool { let waker = self.waker(); let mut cx = Context::from_waker(&waker); - // safety: the waker owns its task and everythig is single threaded - let fut = unsafe { &mut *self.task.get() }; - - match Pin::new(fut).poll(&mut cx) { + match self.task.borrow_mut().as_mut().poll(&mut cx) { Poll::Ready(_) => true, _ => false, } @@ -34,6 +31,15 @@ impl LocalTask { } impl Scheduler { + /// Start a new future on the same thread as the rest of the VirtualDom. + /// + /// This future will not contribute to suspense resolving, so you should primarily use this for reacting to changes + /// and long running tasks. + /// + /// Whenever the component that owns this future is dropped, the future will be dropped as well. + /// + /// Spawning a future onto the root scope will cause it to be dropped when the root component is dropped - which + /// will only occur when the VirtuaalDom itself has been dropped. pub fn spawn(&self, scope: ScopeId, task: impl Future + 'static) -> TaskId { let mut tasks = self.tasks.borrow_mut(); let entry = tasks.vacant_entry(); @@ -42,7 +48,7 @@ impl Scheduler { entry.insert(Rc::new(LocalTask { id: task_id, tx: self.sender.clone(), - task: UnsafeCell::new(Box::pin(task)), + task: RefCell::new(Box::pin(task)), scope, })); @@ -53,9 +59,11 @@ impl Scheduler { task_id } - // drops the future + /// Drop the future with the given TaskId + /// + /// This does nto abort the task, so you'll want to wrap it in an aborthandle if that's important to you pub fn remove(&self, id: TaskId) { - // + self.tasks.borrow_mut().remove(id.0); } } diff --git a/packages/core/src/scheduler/wait.rs b/packages/core/src/scheduler/wait.rs index 0ac312b61..4d954bedf 100644 --- a/packages/core/src/scheduler/wait.rs +++ b/packages/core/src/scheduler/wait.rs @@ -4,10 +4,10 @@ use std::task::{Context, Poll}; use crate::{ factory::RenderReturn, innerlude::{Mutation, Mutations, SuspenseContext}, - ScopeId, TaskId, VNode, VirtualDom, + TaskId, VNode, VirtualDom, }; -use super::{waker::RcWake, SuspenseId, SuspenseLeaf}; +use super::{waker::RcWake, SuspenseId}; impl VirtualDom { /// Handle notifications by tasks inside the scheduler diff --git a/packages/core/src/scope_arena.rs b/packages/core/src/scope_arena.rs index b0b9de04b..cc5783e59 100644 --- a/packages/core/src/scope_arena.rs +++ b/packages/core/src/scope_arena.rs @@ -65,8 +65,11 @@ impl VirtualDom { } pub(crate) fn run_scope(&mut self, scope_id: ScopeId) -> &RenderReturn { + println!("run_scope: {:?}", scope_id); + let mut new_nodes = unsafe { let scope = &mut self.scopes[scope_id.0]; + println!("run_scope: scope: {:?}", scope.render_cnt.get()); scope.hook_idx.set(0); // safety: due to how we traverse the tree, we know that the scope is not currently aliased @@ -123,17 +126,22 @@ impl VirtualDom { } }; + /* + todo: use proper mutability here + + right now we're aliasing the scope, which is not allowed + */ + let scope = &mut self.scopes[scope_id.0]; - let frame = match scope.render_cnt % 2 { - 0 => &mut scope.node_arena_1, - 1 => &mut scope.node_arena_2, - _ => unreachable!(), - }; + let frame = scope.current_frame(); // set the head of the bump frame let alloced = frame.bump.alloc(new_nodes); frame.node.set(alloced); + // And move the render generation forward by one + scope.render_cnt.set(scope.render_cnt.get() + 1); + // rebind the lifetime now that its stored internally unsafe { mem::transmute(alloced) } } diff --git a/packages/core/src/scopes.rs b/packages/core/src/scopes.rs index 29e23396a..fef76f558 100644 --- a/packages/core/src/scopes.rs +++ b/packages/core/src/scopes.rs @@ -43,7 +43,7 @@ impl<'a, T> std::ops::Deref for Scoped<'a, T> { pub struct ScopeId(pub usize); pub struct ScopeState { - pub(crate) render_cnt: usize, + pub(crate) render_cnt: Cell, pub(crate) node_arena_1: BumpFrame, pub(crate) node_arena_2: BumpFrame, @@ -69,14 +69,14 @@ pub struct ScopeState { impl ScopeState { pub fn current_frame(&self) -> &BumpFrame { - match self.render_cnt % 2 { + match self.render_cnt.get() % 2 { 0 => &self.node_arena_1, 1 => &self.node_arena_2, _ => unreachable!(), } } pub fn previous_frame(&self) -> &BumpFrame { - match self.render_cnt % 2 { + match self.render_cnt.get() % 2 { 1 => &self.node_arena_1, 0 => &self.node_arena_2, _ => unreachable!(), diff --git a/packages/core/src/virtual_dom.rs b/packages/core/src/virtual_dom.rs index f30fd0749..f448340d6 100644 --- a/packages/core/src/virtual_dom.rs +++ b/packages/core/src/virtual_dom.rs @@ -9,9 +9,9 @@ use crate::{ nodes::{Template, TemplateId}, scheduler::{SuspenseBoundary, SuspenseId}, scopes::{ScopeId, ScopeState}, - Attribute, AttributeValue, Element, EventPriority, Scope, SuspenseContext, UiEvent, + AttributeValue, Element, EventPriority, Scope, SuspenseContext, UiEvent, }; -use futures_util::{pin_mut, FutureExt, StreamExt}; +use futures_util::{pin_mut, StreamExt}; use slab::Slab; use std::rc::Rc; use std::{ @@ -244,7 +244,7 @@ impl VirtualDom { )))); // The root component is always a suspense boundary for any async children - // This could be unexpected, so we might rethink this behavior + // This could be unexpected, so we might rethink this behavior later root.provide_context(SuspenseBoundary::new(ScopeId(0))); // the root element is always given element 0 @@ -253,26 +253,36 @@ impl VirtualDom { dom } + /// Get the state for any scope given its ID + /// + /// This is useful for inserting or removing contexts from a scope, or rendering out its root node pub fn get_scope(&self, id: ScopeId) -> Option<&ScopeState> { self.scopes.get(id.0) } + /// Get the single scope at the top of the VirtualDom tree that will always be around + /// + /// This scope has a ScopeId of 0 and is the root of the tree pub fn base_scope(&self) -> &ScopeState { self.scopes.get(0).unwrap() } /// Build the virtualdom with a global context inserted into the base scope + /// + /// This is useful for what is essentially dependency injection, when building the app pub fn with_root_context(self, context: T) -> Self { self.base_scope().provide_context(context); self } - fn mark_dirty_scope(&mut self, id: ScopeId) { + /// Manually mark a scope as requiring a re-render + pub fn mark_dirty_scope(&mut self, id: ScopeId) { let height = self.scopes[id.0].height; self.dirty_scopes.insert(DirtyScope { height, id }); } - fn is_scope_suspended(&self, id: ScopeId) -> bool { + /// Determine whether or not a scope is currently in a suspended state + pub fn is_scope_suspended(&self, id: ScopeId) -> bool { !self.scopes[id.0] .consume_context::() .unwrap() @@ -281,35 +291,29 @@ impl VirtualDom { .is_empty() } - /// Returns true if there is any suspended work left to be done. + /// Determine is the tree is at all suspended. Used by SSR and other outside mechanisms to determine if the tree is + /// ready to be rendered. pub fn has_suspended_work(&self) -> bool { !self.scheduler.leaves.borrow().is_empty() } /// Call a listener inside the VirtualDom with data from outside the VirtualDom. /// - /// This method will identify the appropriate element - /// - /// - /// - /// - /// + /// This method will identify the appropriate element. The data must match up with the listener delcared. Note that + /// this method does not give any indication as to the success of the listener call. If the listener is not found, + /// nothing will happen. /// + /// It is up to the listeners themselves to mark nodes as dirty. /// + /// If you have multiple events, you can call this method multiple times before calling "render_with_deadline" pub fn handle_event( &mut self, name: &str, data: Rc, element: ElementId, bubbles: bool, - // todo: priority is helpful when scheduling work around suspense, but we don't currently use it _priority: EventPriority, ) { - let uievent = UiEvent { - bubbles: Rc::new(Cell::new(bubbles)), - data, - }; - /* ------------------------ The algorithm works by walking through the list of dynamic attributes, checking their paths, and breaking when @@ -321,37 +325,35 @@ impl VirtualDom { If we wanted to do capturing, then we would accumulate all the listeners and call them in reverse order. ---------------------- | <-- yes (is ascendant) - | | | <-- no (is not ascendant) + | | | <-- no (is not direct ascendant) | | <-- yes (is ascendant) - | | | | | <--- target element, break early + | | | | | <--- target element, break early, don't check other listeners | | | <-- no, broke early | <-- no, broke early */ let mut parent_path = self.elements.get(element.0); let mut listeners = vec![]; + // We will clone this later. The data itself is wrapped in RC to be used in callbacks if required + let uievent = UiEvent { + bubbles: Rc::new(Cell::new(bubbles)), + 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 }; let target_path = el_ref.path; - let mut attrs = template.dynamic_attrs.iter().enumerate(); - - while let Some((idx, attr)) = attrs.next() { - pub fn is_path_ascendant(small: &[u8], big: &[u8]) -> bool { + for (idx, attr) in template.dynamic_attrs.iter().enumerate() { + fn is_path_ascendant(small: &[u8], big: &[u8]) -> bool { small.len() >= big.len() && small == &big[..small.len()] } let this_path = template.template.attr_paths[idx]; - println!( - "is {:?} ascendant of {:?} ? {}", - this_path, - target_path, - is_path_ascendant(this_path, target_path) - ); - - println!("{ } - {name}, - {}", attr.name, &attr.name[2..]); - + // listeners are required to be prefixed with "on", but they come back to the virtualdom with that missing if &attr.name[2..] == name && is_path_ascendant(&target_path, &this_path) { listeners.push(&attr.value); @@ -367,11 +369,11 @@ impl VirtualDom { } } + // 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 { listener.borrow_mut()(uievent.clone()); - - // Break if the event doesn't bubble if !uievent.bubbles.get() { return; } @@ -452,13 +454,17 @@ impl VirtualDom { pub fn rebuild<'a>(&'a mut self) -> Mutations<'a> { let mut mutations = Mutations::new(0); - let root_node = unsafe { self.run_scope_extend(ScopeId(0)) }; - match root_node { + match unsafe { self.run_scope_extend(ScopeId(0)) } { + // Rebuilding implies we append the created elements to the root RenderReturn::Sync(Some(node)) => { let m = self.create_scope(ScopeId(0), &mut mutations, node); mutations.push(Mutation::AppendChildren { m }); } - RenderReturn::Sync(None) => {} + // If nothing was rendered, then insert a placeholder element instead + RenderReturn::Sync(None) => { + mutations.push(Mutation::CreatePlaceholder { id: ElementId(1) }); + mutations.push(Mutation::AppendChildren { m: 1 }); + } RenderReturn::Async(_) => unreachable!("Root scope cannot be an async component"), } @@ -475,7 +481,9 @@ impl VirtualDom { // Now run render with deadline but dont even try to poll any async tasks let fut = self.render_with_deadline(std::future::ready(())); pin_mut!(fut); - match fut.poll_unpin(&mut cx) { + + // The root component is not allowed to be async + match fut.poll(&mut cx) { std::task::Poll::Ready(mutations) => mutations, std::task::Poll::Pending => panic!("render_immediate should never return pending"), } @@ -491,20 +499,20 @@ impl VirtualDom { deadline: impl Future, ) -> Mutations<'a> { use futures_util::future::{select, Either}; + pin_mut!(deadline); let mut mutations = Mutations::new(0); - pin_mut!(deadline); loop { // first, unload any complete suspense trees for finished_fiber in self.finished_fibers.drain(..) { let scope = &mut self.scopes[finished_fiber.0]; let context = scope.has_context::().unwrap(); - println!("unloading suspense tree {:?}", context.mutations); mutations.extend(context.mutations.borrow_mut().template_mutations.drain(..)); mutations.extend(context.mutations.borrow_mut().drain(..)); + // TODO: count how many nodes are on the stack? mutations.push(Mutation::ReplaceWith { id: context.placeholder.get().unwrap(), m: 1, @@ -525,36 +533,22 @@ impl VirtualDom { // Wait for suspense, or a deadline if self.dirty_scopes.is_empty() { + // If there's no suspense, then we have no reason to wait if self.scheduler.leaves.borrow().is_empty() { return mutations; } - let (work, deadline) = (self.wait_for_work(), &mut deadline); + // Poll the suspense leaves in the meantime + let work = self.wait_for_work(); pin_mut!(work); - if let Either::Left((_, _)) = select(deadline, work).await { + // If the deadline is exceded (left) then we should return the mutations we have + if let Either::Left((_, _)) = select(&mut deadline, work).await { return mutations; } } } } - - // fn mark_dirty_scope(&mut self, scope_id: ScopeId) { - // let scopes = &self.scopes; - // if let Some(scope) = scopes.get_scope(scope_id) { - // let height = scope.height; - // let id = scope_id.0; - // if let Err(index) = self.dirty_scopes.binary_search_by(|new| { - // let scope = scopes.get_scope(*new).unwrap(); - // let new_height = scope.height; - // let new_id = &scope.scope_id(); - // height.cmp(&new_height).then(new_id.0.cmp(&id)) - // }) { - // self.dirty_scopes.insert(index, scope_id); - // log::info!("mark_dirty_scope: {:?}", self.dirty_scopes); - // } - // } - // } } impl Drop for VirtualDom { diff --git a/packages/desktop/src/controller.rs b/packages/desktop/src/controller.rs index 24734b232..2b765d023 100644 --- a/packages/desktop/src/controller.rs +++ b/packages/desktop/src/controller.rs @@ -1,12 +1,8 @@ use crate::desktop_context::{DesktopContext, UserWindowEvent}; +use crate::events::{decode_event, EventMessage}; use dioxus_core::*; -use dioxus_html::events::*; use futures_channel::mpsc::UnboundedReceiver; use futures_util::StreamExt; -use serde::Deserialize; -use serde_json::from_value; -use std::any::Any; -use std::rc::Rc; use std::{ collections::HashMap, sync::Arc, @@ -19,25 +15,6 @@ use wry::{ webview::WebView, }; -macro_rules! match_data { - ( - $m:ident; - $name:ident; - $( - $tip:ty => $($mname:literal)|* ; - )* - ) => { - match $name { - $( $($mname)|* => { - println!("casting to type {:?}", std::any::TypeId::of::<$tip>()); - let val: $tip = from_value::<$tip>($m).ok()?; - Rc::new(val) as Rc - })* - _ => return None, - } - }; -} - pub(super) struct DesktopController { pub(super) webviews: HashMap, pub(super) pending_edits: Arc>>, @@ -86,9 +63,7 @@ impl DesktopController { if let Ok(value) = serde_json::from_value::(json_value) { let name = value.event.clone(); let el_id = ElementId(value.mounted_dom_id); - let evt = decode_event(value); - - if let Some(evt) = evt { + if let Some(evt) = decode_event(value) { dom.handle_event(&name, evt, el_id, true, EventPriority::Medium); } } @@ -143,44 +118,3 @@ impl DesktopController { } } } - -#[derive(Deserialize)] -struct EventMessage { - contents: serde_json::Value, - event: String, - mounted_dom_id: usize, -} - -fn decode_event(value: EventMessage) -> Option> { - let val = value.contents; - let name = value.event.as_str(); - let el_id = ElementId(value.mounted_dom_id); - type DragData = MouseData; - - let evt = match_data! { val; name; - MouseData => "click" | "contextmenu" | "dblclick" | "doubleclick" | "mousedown" | "mouseenter" | "mouseleave" | "mousemove" | "mouseout" | "mouseover" | "mouseup"; - ClipboardData => "copy" | "cut" | "paste"; - CompositionData => "compositionend" | "compositionstart" | "compositionupdate"; - KeyboardData => "keydown" | "keypress" | "keyup"; - FocusData => "blur" | "focus" | "focusin" | "focusout"; - FormData => "change" | "input" | "invalid" | "reset" | "submit"; - DragData => "drag" | "dragend" | "dragenter" | "dragexit" | "dragleave" | "dragover" | "dragstart" | "drop"; - PointerData => "pointerlockchange" | "pointerlockerror" | "pointerdown" | "pointermove" | "pointerup" | "pointerover" | "pointerout" | "pointerenter" | "pointerleave" | "gotpointercapture" | "lostpointercapture"; - SelectionData => "selectstart" | "selectionchange" | "select"; - TouchData => "touchcancel" | "touchend" | "touchmove" | "touchstart"; - ScrollData => "scroll"; - WheelData => "wheel"; - MediaData => "abort" | "canplay" | "canplaythrough" | "durationchange" | "emptied" - | "encrypted" | "ended" | "interruptbegin" | "interruptend" | "loadeddata" - | "loadedmetadata" | "loadstart" | "pause" | "play" | "playing" | "progress" - | "ratechange" | "seeked" | "seeking" | "stalled" | "suspend" | "timeupdate" - | "volumechange" | "waiting" | "error" | "load" | "loadend" | "timeout"; - AnimationData => "animationstart" | "animationend" | "animationiteration"; - TransitionData => "transitionend"; - ToggleData => "toggle"; - // ImageData => "load" | "error"; - // OtherData => "abort" | "afterprint" | "beforeprint" | "beforeunload" | "hashchange" | "languagechange" | "message" | "offline" | "online" | "pagehide" | "pageshow" | "popstate" | "rejectionhandled" | "storage" | "unhandledrejection" | "unload" | "userproximity" | "vrdisplayactivate" | "vrdisplayblur" | "vrdisplayconnect" | "vrdisplaydeactivate" | "vrdisplaydisconnect" | "vrdisplayfocus" | "vrdisplaypointerrestricted" | "vrdisplaypointerunrestricted" | "vrdisplaypresentchange"; - }; - - Some(evt) -} diff --git a/packages/desktop/src/events.rs b/packages/desktop/src/events.rs index 2d2905474..c407013ea 100644 --- a/packages/desktop/src/events.rs +++ b/packages/desktop/src/events.rs @@ -33,17 +33,61 @@ pub(crate) fn parse_ipc_message(payload: &str) -> Option { } } -#[derive(Deserialize, Serialize)] -struct ImEvent { - event: String, - mounted_dom_id: ElementId, - contents: serde_json::Value, +macro_rules! match_data { + ( + $m:ident; + $name:ident; + $( + $tip:ty => $($mname:literal)|* ; + )* + ) => { + match $name { + $( $($mname)|* => { + println!("casting to type {:?}", std::any::TypeId::of::<$tip>()); + let val: $tip = from_value::<$tip>($m).ok()?; + Rc::new(val) as Rc + })* + _ => return None, + } + }; } -// pub fn make_synthetic_event(name: &str, val: serde_json::Value) -> Option> { -// // right now we don't support the datatransfer in Drag -// type DragData = MouseData; -// type ProgressData = MediaData; +#[derive(Deserialize)] +pub struct EventMessage { + pub contents: serde_json::Value, + pub event: String, + pub mounted_dom_id: usize, +} -// Some(res) -// } +pub fn decode_event(value: EventMessage) -> Option> { + let val = value.contents; + let name = value.event.as_str(); + type DragData = MouseData; + + let evt = match_data! { val; name; + MouseData => "click" | "contextmenu" | "dblclick" | "doubleclick" | "mousedown" | "mouseenter" | "mouseleave" | "mousemove" | "mouseout" | "mouseover" | "mouseup"; + ClipboardData => "copy" | "cut" | "paste"; + CompositionData => "compositionend" | "compositionstart" | "compositionupdate"; + KeyboardData => "keydown" | "keypress" | "keyup"; + FocusData => "blur" | "focus" | "focusin" | "focusout"; + FormData => "change" | "input" | "invalid" | "reset" | "submit"; + DragData => "drag" | "dragend" | "dragenter" | "dragexit" | "dragleave" | "dragover" | "dragstart" | "drop"; + PointerData => "pointerlockchange" | "pointerlockerror" | "pointerdown" | "pointermove" | "pointerup" | "pointerover" | "pointerout" | "pointerenter" | "pointerleave" | "gotpointercapture" | "lostpointercapture"; + SelectionData => "selectstart" | "selectionchange" | "select"; + TouchData => "touchcancel" | "touchend" | "touchmove" | "touchstart"; + ScrollData => "scroll"; + WheelData => "wheel"; + MediaData => "abort" | "canplay" | "canplaythrough" | "durationchange" | "emptied" + | "encrypted" | "ended" | "interruptbegin" | "interruptend" | "loadeddata" + | "loadedmetadata" | "loadstart" | "pause" | "play" | "playing" | "progress" + | "ratechange" | "seeked" | "seeking" | "stalled" | "suspend" | "timeupdate" + | "volumechange" | "waiting" | "error" | "load" | "loadend" | "timeout"; + AnimationData => "animationstart" | "animationend" | "animationiteration"; + TransitionData => "transitionend"; + ToggleData => "toggle"; + // ImageData => "load" | "error"; + // OtherData => "abort" | "afterprint" | "beforeprint" | "beforeunload" | "hashchange" | "languagechange" | "message" | "offline" | "online" | "pagehide" | "pageshow" | "popstate" | "rejectionhandled" | "storage" | "unhandledrejection" | "unload" | "userproximity" | "vrdisplayactivate" | "vrdisplayblur" | "vrdisplayconnect" | "vrdisplaydeactivate" | "vrdisplaydisconnect" | "vrdisplayfocus" | "vrdisplaypointerrestricted" | "vrdisplaypointerunrestricted" | "vrdisplaypresentchange"; + }; + + Some(evt) +} diff --git a/packages/interpreter/src/interpreter.js b/packages/interpreter/src/interpreter.js index 85b4de8dd..a663f9278 100644 --- a/packages/interpreter/src/interpreter.js +++ b/packages/interpreter/src/interpreter.js @@ -311,7 +311,7 @@ export class Interpreter { this.CreateElementNs(edit.name, edit.id, edit.ns); break; case "SetText": - this.SetText(edit.id, edit.text); + this.SetText(edit.id, edit.value); break; case "SetAttribute": this.SetAttribute(edit.id, edit.name, edit.value, edit.ns);