Feat: move webview logic into library

This commit is contained in:
Jonathan Kelley committed 2021-02-25 18:44:00 -05:00
1 parent 18a7a1f9c4
commit 32b45e5ba1
10 files changed
+249 -514

No files matched your search

-3
View File
@@ -1,3 +0,0 @@
// This file interperets incoming edit messages into the page and renders them.
// This is written in javascript to be slightly easier to work with (instead of having to pre-compile the wasm)
// One day, we'll probably using wasmbindgen for the inner, but a javascripty one is fine too.
+43 -143
View File
@@ -1,152 +1,52 @@
//! An example where the dioxus vdom is running in a native thread, interacting with webview
//! Content is passed from the native thread into the webview
use std::{
borrow::BorrowMut,
rc::Rc,
sync::{mpsc::channel, Arc},
};
use dioxus_core::prelude::*;
// use async_std::{channel, task::block_on};
use dioxus_core::{dodriodiff::DiffMachine, prelude::bumpalo::Bump, prelude::*, scope};
use scope::Scope;
use web_view::Handle;
static HTML_CONTENT: &'static str = include_str!("./index.html");
enum InnerEvent {
Initiate(Handle<()>),
}
// async_std::task::spawn(async {
// #[async_std::main]
fn main() -> anyhow::Result<()> {
let (sender, receiver) = channel::<InnerEvent>();
// let (sender, receiver) = channel::unbounded::<InnerEvent>();
// let task = async_std::task::spawn(async move {
let mut view = web_view::builder()
.title("My Project")
.content(web_view::Content::Html(HTML_CONTENT))
.size(320, 480)
.resizable(true)
.debug(true)
.user_data(())
.invoke_handler(|view, arg| {
// todo: handle events here
println!("handling invoker");
let handle = view.handle();
sender.send(InnerEvent::Initiate(handle));
Ok(())
})
.build()
.unwrap();
println!("building the diff");
let bump = Bump::new();
let mut diff_machine = DiffMachine::new(&bump);
let old = html! {<div> </div>}(&bump);
// let mut scope = Scope::new(TEST, (), None);
// scope.run::<()>();
let new = html! {
<div>
<div class="flex items-center justify-center flex-col">
<div class="flex items-center justify-center">
<div class="flex flex-col bg-white rounded p-4 w-full max-w-xs">
// Title
<div class="font-bold text-xl">
"Jon's awesome site!!11"
</div>
// Subtext / description
<div class="text-sm text-gray-500">
"He worked so hard on it :)"
</div>
<div class="flex flex-row items-center justify-center mt-6">
// Main number
<div class="font-medium text-6xl">
"1337"
fn main(){
dioxus_webview::launch(
// Customize the webview
|builder| {
builder
.title("Test Dioxus App")
.size(320, 480)
.resizable(true)
.debug(true)
},
// Props
(),
// Draw the root component
|ctx, _props| {
ctx.view(html! {
<div>
<div class="flex items-center justify-center flex-col">
<div class="flex items-center justify-center">
<div class="flex flex-col bg-white rounded p-4 w-full max-w-xs">
// Title
<div class="font-bold text-xl"> "Jon's awesome site!!11" </div>
// Subtext / description
<div class="text-sm text-gray-500"> "He worked so hard on it :)" </div>
<div class="flex flex-row items-center justify-center mt-6">
// Main number
<div class="font-medium text-6xl">
"1337"
</div>
</div>
// Try another
<div class="flex flex-row justify-between mt-6">
<a href="http://localhost:8080/fib/{}" class="underline">
"Legit made my own React"
</a>
</div>
</div>
</div>
// Try another
<div class="flex flex-row justify-between mt-6">
// <a href=format!("http://localhost:8080/fib/{}", other_fib_to_try) class="underline">
"Legit made my own React"
// </a>
</div>
</div>
</div>
</div>
</div>
}(&bump);
diff_machine.diff_node(&old, &new);
let edits = diff_machine.consume();
let ready_edits = serde_json::to_string(&edits)?;
let ref_edits = Arc::new(ready_edits);
loop {
view.step().expect("should not fail");
// if let Some(evt) = receiver.try_recv() {}
if let Ok(event) = receiver.try_recv() {
match event {
InnerEvent::Initiate(handle) => {
// println!("awesome, things worked");
let ediits = ref_edits.clone();
// println!("{}", ediits);
handle
.dispatch(move |view| {
view.eval(format!("EditListReceived(`{}`);", ediits).as_str())?;
Ok(())
})
.expect("Dispatch failed");
// let g = handle.();
}
}
}
// let event = receiver.try_recv();
// view.eval("alert('omg');")?;
// view.step().expect("webview should not fail")?;
}
})
},
).expect("Webview finished");
}
// static TEST: FC<()> = |ctx, props| {
// ctx.view(html! {
// <div>
// <div class="flex items-center justify-center flex-col">
// <div class="flex items-center justify-center">
// <div class="flex flex-col bg-white rounded p-4 w-full max-w-xs">
// // Title
// <div class="font-bold text-xl">
// "Jon's awesome site!!11"
// </div>
// // Subtext / description
// <div class="text-sm text-gray-500">
// "He worked so hard on it :)"
// </div>
// <div class="flex flex-row items-center justify-center mt-6">
// // Main number
// <div class="font-medium text-6xl">
// "1337"
// </div>
// </div>
// // Try another
// <div class="flex flex-row justify-between mt-6">
// // <a href=format!("http://localhost:8080/fib/{}", other_fib_to_try) class="underline">
// "Legit made my own React"
// // </a>
// </div>
// </div>
// </div>
// </div>
// </div>
// })
// };
@@ -12,34 +12,6 @@
<div></div>
</body>
<script>
// #[derive(Debug, serde::Serialize, serde::Deserialize)]
// pub enum Edit<'d> {
// SetText { text: &'d str },
// RemoveSelfAndNextSiblings {},
// ReplaceWith,
// SetAttribute { name: &'d str, value: &'d str },
// RemoveAttribute { name: &'d str },
// PushReverseChild { n: u32 },
// PopPushChild { n: u32 },
// Pop,
// AppendChild,
// CreateTextNode { text: &'d str },
// CreateElement { tag_name: &'d str },
// NewEventListener { event_type: &'d str, a: u32, b: u32 },
// UpdateEventListener { event_type: &'d str, a: u32, b: u32 },
// RemoveEventListener { event_type: &'d str },
// CreateElementNs { tag_name: &'d str, ns: &'d str },
// SaveChildrenToTemporaries { temp: u32, start: u32, end: u32 },
// PushChild { n: u32 },
// PushTemporary { temp: u32 },
// InsertBefore,
// PopPushReverseChild { n: u32 },
// RemoveChild { n: u32 },
// SetClass { class_name: &'d str },
// }
// const interpreter = new Interpreter();
class Interpreter {
constructor(root) {
this.stack = [root];
@@ -302,20 +274,13 @@
}
}
let op = new OPTABLE();
const c = window.document.body.firstChild;
console.log("First child");
console.log(c);
const op_table = new OPTABLE();
const interpreter = new Interpreter(window.document.body);
function EditListReceived(rawEditList) {
let editList = JSON.parse(rawEditList);
console.log(editList);
editList.forEach(function (edit, index) {
// console.log("processing edit");
const name = edit.type;
console.log(name);
const f = op[name];
f(edit, interpreter);
op_table[edit.type](edit, interpreter);
});
}
+167 -41
View File
@@ -1,8 +1,18 @@
use dioxus_core::prelude::*;
use web_view::WebViewBuilder;
use std::sync::mpsc::channel;
use std::sync::Arc;
pub fn new<T>(root: FC<T>) -> WebviewRenderer<T> {
WebviewRenderer::new(root)
use dioxus_core::prelude::*;
use web_view::Handle;
use web_view::{WVResult, WebView, WebViewBuilder};
static HTML_CONTENT: &'static str = include_str!("./index.html");
pub fn launch<T: 'static>(
builder: impl FnOnce(DioxusWebviewBuilder) -> DioxusWebviewBuilder,
props: T,
root: FC<T>,
) -> anyhow::Result<()> {
WebviewRenderer::run(root, props, builder)
}
/// The `WebviewRenderer` provides a way of rendering a Dioxus Virtual DOM through a bridge to a Webview instance.
@@ -11,13 +21,78 @@ pub struct WebviewRenderer<T> {
/// The root component used to render the Webview
root: FC<T>,
}
enum InnerEvent {
Initiate(Handle<()>),
}
impl<T: 'static> WebviewRenderer<T> {
pub fn run(
root: FC<T>,
props: T,
user_builder: impl FnOnce(DioxusWebviewBuilder) -> DioxusWebviewBuilder,
) -> anyhow::Result<()> {
let (sender, receiver) = channel::<InnerEvent>();
let DioxusWebviewBuilder {
title,
width,
height,
resizable,
debug,
frameless,
visible,
min_width,
min_height,
} = user_builder(DioxusWebviewBuilder::new());
let mut view = web_view::builder()
.invoke_handler(|view, arg| {
let handle = view.handle();
sender
.send(InnerEvent::Initiate(handle))
.expect("should not fail");
Ok(())
})
.content(web_view::Content::Html(HTML_CONTENT))
.user_data(())
.title(title)
.size(width, height)
.resizable(resizable)
.debug(debug)
.frameless(frameless)
.visible(visible)
.min_size(min_width, min_height)
.build()
.unwrap();
let mut vdom = VirtualDom::new_with_props(root, props);
let edits = vdom.rebuild()?;
let ref_edits = Arc::new(serde_json::to_string(&edits)?);
loop {
view.step()
.expect("should not fail")
.expect("should not fail");
if let Ok(event) = receiver.try_recv() {
if let InnerEvent::Initiate(handle) = event {
let editlist = ref_edits.clone();
handle
.dispatch(move |view| {
view.eval(format!("EditListReceived(`{}`);", editlist).as_str())
})
.expect("Dispatch failed");
}
}
}
}
impl<T> WebviewRenderer<T> {
/// Create a new text-renderer instance from a functional component root.
/// Automatically progresses the creation of the VNode tree to completion.
///
/// A VDom is automatically created. If you want more granular control of the VDom, use `from_vdom`
pub fn new(root: FC<T>) -> Self {
pub fn new(root: FC<T>, builder: impl FnOnce() -> WVResult<WebView<'static, ()>>) -> Self {
Self { root }
}
@@ -36,41 +111,92 @@ impl<T> WebviewRenderer<T> {
pub fn update_mut(&mut self, modifier: impl Fn(&mut T)) {
todo!()
}
pub fn launch(self, props: T) {
todo!()
// let mut ctx = Context { props: &props };
// let WebviewRenderer { root } = self;
// let content = root(&mut ctx);
// let html_content = content.to_string();
// /*
// TODO: @Jon
// Launch the webview with a premade VDom app
// */
// web_view::builder()
// .title("My Project")
// .content(web_view::Content::Html(html_content))
// .size(320, 480)
// .resizable(true)
// .debug(true)
// .user_data(())
// .invoke_handler(|_webview, _arg| Ok(()))
// .run()
// .unwrap();
}
}
// mod receiver {
// use dioxus_core::prelude::*;
pub struct DioxusWebviewBuilder<'a> {
pub(crate) title: &'a str,
pub(crate) width: i32,
pub(crate) height: i32,
pub(crate) resizable: bool,
pub(crate) debug: bool,
pub(crate) frameless: bool,
pub(crate) visible: bool,
pub(crate) min_width: i32,
pub(crate) min_height: i32,
}
impl<'a> DioxusWebviewBuilder<'a> {
fn new() -> Self {
#[cfg(debug_assertions)]
let debug = true;
#[cfg(not(debug_assertions))]
let debug = false;
// /// The receiver app is a container that builds a connection to the host process that shuttles events and patches.
// pub(crate) static ReceiverApp: FC<()> = |ctx| {
// //
// html! {
// <div>
// {}
// </div>
// }
// };
// }
DioxusWebviewBuilder {
title: "Application",
width: 800,
height: 600,
resizable: true,
debug,
frameless: false,
visible: true,
min_width: 300,
min_height: 300,
}
}
/// Sets the title of the WebView window.
///
/// Defaults to `"Application"`.
pub fn title(mut self, title: &'a str) -> Self {
self.title = title;
self
}
/// Sets the size of the WebView window.
///
/// Defaults to 800 x 600.
pub fn size(mut self, width: i32, height: i32) -> Self {
self.width = width;
self.height = height;
self
}
/// Sets the resizability of the WebView window. If set to false, the window cannot be resized.
///
/// Defaults to `true`.
pub fn resizable(mut self, resizable: bool) -> Self {
self.resizable = resizable;
self
}
/// Enables or disables debug mode.
///
/// Defaults to `true` for debug builds, `false` for release builds.
pub fn debug(mut self, debug: bool) -> Self {
self.debug = debug;
self
}
/// The window crated will be frameless
///
/// defaults to `false`
pub fn frameless(mut self, frameless: bool) -> Self {
self.frameless = frameless;
self
}
/// Set the visibility of the WebView window.
///
/// defaults to `true`
pub fn visible(mut self, visible: bool) -> Self {
self.visible = visible;
self
}
/// Sets the minimum size of the WebView window.
///
/// Defaults to 300 x 300.
pub fn min_size(mut self, width: i32, height: i32) -> Self {
self.min_width = width;
self.min_height = height;
self
}
}