feat: wire up event delegator for webview
This commit is contained in:
1 parent
0a907b35c6
commit
7dfe89c958
21 files changed
+265
-272
No files matched your search
@@ -0,0 +1,85 @@
|
||||
//! Convert a serialized event to an event Trigger
|
||||
//!
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
use dioxus_core::{
|
||||
events::{
|
||||
on::{MouseEvent, MouseEventInner},
|
||||
VirtualEvent,
|
||||
},
|
||||
ElementId, EventPriority, EventTrigger, ScopeId,
|
||||
};
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
struct ImEvent {
|
||||
event: String,
|
||||
mounted_dom_id: u64,
|
||||
scope: u64,
|
||||
}
|
||||
pub fn trigger_from_serialized(val: serde_json::Value) -> EventTrigger {
|
||||
let mut data: Vec<ImEvent> = serde_json::from_value(val).unwrap();
|
||||
let data = data.drain(..).next().unwrap();
|
||||
|
||||
let event = VirtualEvent::MouseEvent(MouseEvent(Rc::new(WebviewMouseEvent)));
|
||||
let scope = ScopeId(data.scope as usize);
|
||||
let mounted_dom_id = Some(ElementId(data.mounted_dom_id as usize));
|
||||
let priority = EventPriority::High;
|
||||
EventTrigger::new(event, scope, mounted_dom_id, priority)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct WebviewMouseEvent;
|
||||
impl MouseEventInner for WebviewMouseEvent {
|
||||
fn alt_key(&self) -> bool {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn button(&self) -> i16 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn buttons(&self) -> u16 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn client_x(&self) -> i32 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn client_y(&self) -> i32 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn ctrl_key(&self) -> bool {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn meta_key(&self) -> bool {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn page_x(&self) -> i32 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn page_y(&self) -> i32 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn screen_x(&self) -> i32 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn screen_y(&self) -> i32 {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn shift_key(&self) -> bool {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn get_modifier_state(&self, key_code: &str) -> bool {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
+108
-45
@@ -5,10 +5,13 @@
|
||||
<script>
|
||||
class Interpreter {
|
||||
constructor(root) {
|
||||
this.root = root;
|
||||
this.stack = [root];
|
||||
this.listeners = {};
|
||||
this.listeners = {
|
||||
"onclick": {}
|
||||
};
|
||||
this.lastNodeWasText = false;
|
||||
this.nodes = [];
|
||||
this.nodes = [root, root, root, root];
|
||||
}
|
||||
|
||||
top() {
|
||||
@@ -18,66 +21,125 @@
|
||||
pop() {
|
||||
return this.stack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
class OPTABLE {
|
||||
PushRoot(self, edit) {
|
||||
const id = edit.root;
|
||||
const node = self.nodes[id];
|
||||
self.stack.push(node);
|
||||
PushRoot(edit) {
|
||||
const id = edit.id;
|
||||
const node = this.nodes[id];
|
||||
console.log("pushing root ", node, "with id", id);
|
||||
this.stack.push(node);
|
||||
}
|
||||
AppendChildren(self, edit) {
|
||||
let root = self.stack[self.stack.length - (edit.many + 1)];
|
||||
|
||||
AppendChildren(edit) {
|
||||
let root = this.stack[this.stack.length - (edit.many + 1)];
|
||||
for (let i = 0; i < edit.many; i++) {
|
||||
console.log("popping ", i, edit.many);
|
||||
let node = self.pop();
|
||||
let node = this.pop();
|
||||
root.appendChild(node);
|
||||
}
|
||||
}
|
||||
ReplaceWith(self, edit) {
|
||||
|
||||
let root = self.stack[self.stack.length - (edit.many + 1)];
|
||||
ReplaceWith(edit) {
|
||||
|
||||
let root = this.stack[this.stack.length - (edit.many + 1)];
|
||||
let els = [];
|
||||
|
||||
for (let i = 0; i < edit.many; i++) {
|
||||
els.push(self.pop());
|
||||
els.push(this.pop());
|
||||
}
|
||||
|
||||
root.replaceWith(...els);
|
||||
}
|
||||
Remove(self, edit) {
|
||||
const node = self.stack.pop();
|
||||
|
||||
Remove(edit) {
|
||||
const node = this.stack.pop();
|
||||
node.remove();
|
||||
}
|
||||
RemoveAllChildren(self, edit) {}
|
||||
CreateTextNode(self, edit) {
|
||||
self.stack.push(document.createTextNode(edit.text));
|
||||
|
||||
RemoveAllChildren(edit) {}
|
||||
|
||||
CreateTextNode(edit) {
|
||||
const node = document.createTextNode(edit.text);
|
||||
this.nodes[edit.id] = node;
|
||||
this.stack.push(node);
|
||||
}
|
||||
CreateElement(self, edit) {
|
||||
|
||||
CreateElement(edit) {
|
||||
const tagName = edit.tag;
|
||||
const el = document.createElement(tagName);
|
||||
this.nodes[edit.id] = el;
|
||||
console.log(`creating element: `, edit);
|
||||
self.stack.push(document.createElement(tagName));
|
||||
this.stack.push(el);
|
||||
}
|
||||
CreateElementNs(self, edit) {
|
||||
|
||||
CreateElementNs(edit) {
|
||||
const tagName = edit.tag;
|
||||
console.log(`creating namespaced element: `, edit);
|
||||
self.stack.push(document.createElementNS(edit.ns, edit.tag));
|
||||
this.stack.push(document.createElementNS(edit.ns, edit.tag));
|
||||
}
|
||||
CreatePlaceholder(self, edit) {
|
||||
const a = `self.stack.push(document.createElement("pre"))`;
|
||||
self.stack.push(document.createComment("vroot"));
|
||||
|
||||
CreatePlaceholder(edit) {
|
||||
const a = `this.stack.push(document.createElement(" pre"))`;
|
||||
this.stack.push(document.createComment("vroot"));
|
||||
}
|
||||
NewEventListener(self, edit) {}
|
||||
RemoveEventListener(self, edit) {}
|
||||
SetText(self, edit) {
|
||||
self.top().textContent = edit.text;
|
||||
|
||||
NewEventListener(edit) {
|
||||
const element_id = edit.element_id;
|
||||
const event_name = edit.event_name;
|
||||
const mounted_node_id = edit.mounted_node_id;
|
||||
const scope = edit.scope;
|
||||
|
||||
const element = this.top();
|
||||
element.setAttribute(`dioxus-event-${event_name}`, `${scope}.${mounted_node_id}`);
|
||||
|
||||
console.log("listener map is", this.listeners);
|
||||
if (this.listeners[event_name] === undefined) {
|
||||
console.log("adding listener!");
|
||||
this.listeners[event_name] = "bla";
|
||||
this.root.addEventListener(event_name, (event) => {
|
||||
const target = event.target;
|
||||
const type = event.type;
|
||||
const val = target.getAttribute(`dioxus-event-${event_name}`);
|
||||
const fields = val.split(".");
|
||||
const scope_id = parseInt(fields[0]);
|
||||
const real_id = parseInt(fields[1]);
|
||||
|
||||
console.log(`parsed event with scope_id ${scope_id} and real_id ${real_id}`);
|
||||
|
||||
rpc.call('user_event', {
|
||||
event: event_name,
|
||||
scope: scope_id,
|
||||
mounted_dom_id: real_id,
|
||||
}).then((reply) => {
|
||||
console.log(reply);
|
||||
this.stack.push(this.root);
|
||||
|
||||
for (let x = 0; x < reply.length; x++) {
|
||||
let edit = reply[x];
|
||||
console.log(edit);
|
||||
|
||||
let f = this[edit.type];
|
||||
f.call(this, edit);
|
||||
}
|
||||
|
||||
console.log("initiated");
|
||||
}).catch((err) => {
|
||||
console.log("failed to initiate", err);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
SetAttribute(self, edit) {
|
||||
|
||||
RemoveEventListener(edit) {}
|
||||
|
||||
SetText(edit) {
|
||||
this.top().textContent = edit.text;
|
||||
}
|
||||
|
||||
SetAttribute(edit) {
|
||||
const name = edit.field;
|
||||
const value = edit.value;
|
||||
const ns = edit.ns;
|
||||
|
||||
const node = self.top(self.stack);
|
||||
const node = this.top(this.stack);
|
||||
if (ns == "style") {
|
||||
node.style[name] = value;
|
||||
} else if (ns !== undefined) {
|
||||
@@ -85,36 +147,35 @@
|
||||
} else {
|
||||
node.setAttribute(name, value);
|
||||
}
|
||||
|
||||
if ((name === "value", self)) {
|
||||
if (name === "value") {
|
||||
node.value = value;
|
||||
}
|
||||
if ((name === "checked", self)) {
|
||||
if (name === "checked") {
|
||||
node.checked = true;
|
||||
}
|
||||
if ((name === "selected", self)) {
|
||||
if (name === "selected") {
|
||||
node.selected = true;
|
||||
}
|
||||
}
|
||||
RemoveAttribute(self, edit) {
|
||||
RemoveAttribute(edit) {
|
||||
const name = edit.field;
|
||||
const node = self.top(self.stack);
|
||||
|
||||
const node = this.top(this.stack);
|
||||
node.removeAttribute(name);
|
||||
|
||||
if ((name === "value", self)) {
|
||||
if (name === "value") {
|
||||
node.value = null;
|
||||
}
|
||||
if ((name === "checked", self)) {
|
||||
if (name === "checked") {
|
||||
node.checked = false;
|
||||
}
|
||||
if ((name === "selected", self)) {
|
||||
if (name === "selected") {
|
||||
node.selected = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const op_table = new OPTABLE();
|
||||
|
||||
|
||||
|
||||
async function initialize() {
|
||||
const reply = await rpc.call('initiate');
|
||||
@@ -124,7 +185,9 @@
|
||||
for (let x = 0; x < reply.length; x++) {
|
||||
let edit = reply[x];
|
||||
console.log(edit);
|
||||
op_table[edit.type](interpreter, edit);
|
||||
|
||||
let f = interpreter[edit.type];
|
||||
f.call(interpreter, edit);
|
||||
}
|
||||
|
||||
console.log("stack completed: ", interpreter.stack);
|
||||
|
||||
+34
-13
@@ -17,6 +17,8 @@ use wry::{
|
||||
|
||||
mod dom;
|
||||
mod escape;
|
||||
mod events;
|
||||
use events::*;
|
||||
|
||||
static HTML_CONTENT: &'static str = include_str!("./index.html");
|
||||
|
||||
@@ -59,6 +61,7 @@ impl<T: Properties + 'static> WebviewRenderer<T> {
|
||||
user_builder: impl FnOnce(WindowBuilder) -> WindowBuilder,
|
||||
redits: Option<Vec<DomEdit<'static>>>,
|
||||
) -> anyhow::Result<()> {
|
||||
log::info!("hello edits");
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
let window = user_builder(WindowBuilder::new()).build(&event_loop)?;
|
||||
@@ -101,24 +104,42 @@ impl<T: Properties + 'static> WebviewRenderer<T> {
|
||||
Some(RpcResponse::new_result(req.id.take(), Some(edits)))
|
||||
}
|
||||
"user_event" => {
|
||||
let mut lock = vdom.write().unwrap();
|
||||
let mut reg_lock = registry.write().unwrap();
|
||||
log::debug!("User event received");
|
||||
|
||||
// Create the thin wrapper around the registry to collect the edits into
|
||||
let mut real = dom::WebviewDom::new(reg_lock.take().unwrap());
|
||||
let registry = registry.clone();
|
||||
let vdom = vdom.clone();
|
||||
let response = async_std::task::block_on(async move {
|
||||
let mut lock = vdom.write().unwrap();
|
||||
let mut reg_lock = registry.write().unwrap();
|
||||
|
||||
// Serialize the edit stream
|
||||
let edits = {
|
||||
// a deserialized event
|
||||
let data = req.params.unwrap();
|
||||
log::debug!("Data: {:#?}", data);
|
||||
let event = trigger_from_serialized(data);
|
||||
|
||||
lock.queue_event(event);
|
||||
|
||||
// Create the thin wrapper around the registry to collect the edits into
|
||||
let mut real = dom::WebviewDom::new(reg_lock.take().unwrap());
|
||||
|
||||
// Serialize the edit stream
|
||||
//
|
||||
let mut edits = Vec::new();
|
||||
lock.rebuild(&mut real, &mut edits).unwrap();
|
||||
serde_json::to_value(edits).unwrap()
|
||||
};
|
||||
lock.progress_with_event(&mut real, &mut edits)
|
||||
.await
|
||||
.expect("failed to progress");
|
||||
let edits = serde_json::to_value(edits).unwrap();
|
||||
|
||||
// Give back the registry into its slot
|
||||
*reg_lock = Some(real.consume());
|
||||
// Give back the registry into its slot
|
||||
*reg_lock = Some(real.consume());
|
||||
|
||||
// Return the edits into the webview runtime
|
||||
Some(RpcResponse::new_result(req.id.take(), Some(edits)))
|
||||
// Return the edits into the webview runtime
|
||||
Some(RpcResponse::new_result(req.id.take(), Some(edits)))
|
||||
});
|
||||
|
||||
response
|
||||
|
||||
// spawn a task to clean up the garbage
|
||||
}
|
||||
_ => todo!("this message failed"),
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user