chore: cleanup workspace
This commit is contained in:
1 parent
3bedcb93ca
commit
8f0bb5dc5b
54 files changed
+691
-1822
No files matched your search
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"rust-analyzer.inlayHints.enable": false
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
# dioxus webview arch
|
||||
|
||||
|
||||
dioxus webview represents a usecase of dioxus to stream edits from one dom to another. In this particular case, we stream edits from a native process into the webview's runtime. WebView is really portable, so we can leverage the UI of iOS, Android, and all desktop platforms while also maintaining direct access to platform-specific APIs. It's the best of both worlds!
|
||||
|
||||
|
||||
For this to work properly, we need to ship a universal client that receives Dioxus edits from a remote process. To make our lives (as developers) a bit easier, the client code is currently implemented in JS (subject to change!).
|
||||
|
||||
The provider (the thing that talks to the client on the other side of the network) is still written in Rust, and is supplied through the dioxus-webview crate. The client is much more "dumb" than the provider - the provider must handle NodeID generation, flushing changes, priorities, managing events, and more.
|
||||
|
||||
|
||||
## update; wry!
|
||||
|
||||
Wry is an effort by the Tauri team to make fully native
|
||||
@@ -0,0 +1,41 @@
|
||||
[package]
|
||||
name = "dioxus-desktop"
|
||||
version = "0.0.0"
|
||||
authors = ["Jonathan Kelley <jkelleyrtp@gmail.com>"]
|
||||
edition = "2018"
|
||||
description = "Dioxus VirtualDOM renderer for a remote webview instance"
|
||||
license = "MIT/Apache-2.0"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
# web-view = { git = "https://github.com/Boscop/web-view" }
|
||||
dioxus-core = { path = "../core", version = "0.1.2", features = ["serialize"] }
|
||||
anyhow = "1.0.38"
|
||||
argh = "0.1.4"
|
||||
serde = "1.0.120"
|
||||
serde_json = "1.0.61"
|
||||
thiserror = "1.0.23"
|
||||
log = "0.4.13"
|
||||
fern = { version = "0.6.0", features = ["colored"] }
|
||||
html-escape = "0.2.9"
|
||||
wry = "0.11.0"
|
||||
|
||||
|
||||
[dev-dependencies]
|
||||
tide = "0.15.0"
|
||||
tide-websockets = "0.3.0"
|
||||
async-std = { version = "1.9.0", features = ["attributes"] }
|
||||
|
||||
# thiserror = "1.0.23"
|
||||
# log = "0.4.13"
|
||||
# fern = { version = "0.6.0", features = ["colored"] }
|
||||
# wasm-bindgen-cli-support = "0.2.71"
|
||||
# anyhow = "1.0.38"
|
||||
# argh = "0.1.4"
|
||||
# async-std = { version = "1.9.0", features = ["attributes"] }
|
||||
# serde = "1.0.120"
|
||||
# serde_json = "1.0.61"
|
||||
|
||||
|
||||
[build-dependencies]
|
||||
@@ -0,0 +1,47 @@
|
||||
# Dioxus-webview
|
||||
|
||||
Dioxus-webview bridges virtual and Webview DOMs together to make simple, portable, desktop applications.
|
||||
|
||||
Dioxus-webview is an attempt at making a simpler "Tauri" where creating desktop applications is as simple as:
|
||||
|
||||
```rust
|
||||
// main.rs
|
||||
#[async_std::main]
|
||||
async fn main() {
|
||||
dioxus_webview::new(|cx| {
|
||||
let (count, set_count) = use_state(cx, || 0);
|
||||
cx.render(html! {
|
||||
<div>
|
||||
<h1> "Dioxus Desktop Demo" </h1>
|
||||
<p> "Count is {count}"</p>
|
||||
<button onclick=|_| set_count(count + 1) >
|
||||
"Click to increment"
|
||||
</button>
|
||||
</div>
|
||||
})
|
||||
})
|
||||
.configure_webview(|view| {
|
||||
// custom webview config options
|
||||
})
|
||||
.launch()
|
||||
.await;
|
||||
}
|
||||
```
|
||||
|
||||
and then to create a native .app:
|
||||
|
||||
```
|
||||
dioxus bundle --platform macOS
|
||||
```
|
||||
|
||||
## Goals
|
||||
|
||||
Because the host VirtualDOM is running in its own native process, native applications can unlock their full potential. Dioxus-webview is designed to be a 100% rust alternative to ElectronJS without the memory overhead or bloat of ElectronJS apps.
|
||||
|
||||
By bridging the native process, desktop apps can access full multithreading power, peripheral support, hardware access, and native filesystem controls without the hassle of web technologies. Our goal with Dioxus-webview is to make it easy to ship both a web and native application, and quickly see large performance boosts without having to re-write the whole stack. As the dioxus ecosystem grows, we hope to see 3rd parties providing wrappers for storage, offline mode, etc that supports both web and native technologies.
|
||||
|
||||
## Tech
|
||||
|
||||
Dioxus-desktop is a pure liveview application where all of the state and event handlers are proxied through the liveview and into the native process. For pure server-based liveview, this would normally be too slow (in both render performance and latency), but because the VDom is local, desktop apps are just as fast as Electron.
|
||||
|
||||
Dioxus-desktop leverages dioxus-liveview under the hood, but with convenience wrappers around setting up the VDom bridge, proxying events, and serving the initial WebSys-Renderer. The backend is served by Tide, so an async runtime _is_ needed - we recommend async-std in Tokio mode.
|
||||
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "webview-client"
|
||||
version = "0.1.0"
|
||||
authors = ["Jonathan Kelley <jkelleyrtp@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
dioxus-core = {path = "../../core"}
|
||||
@@ -0,0 +1,64 @@
|
||||
use dioxus_core::patch::Edit;
|
||||
use dioxus_core::prelude::*;
|
||||
|
||||
static SERVER_RENDERED_KEY: &'static str = "abc123";
|
||||
#[derive(Debug, PartialEq, Props)]
|
||||
struct ServerRendered {
|
||||
name0: String,
|
||||
name1: String,
|
||||
name2: String,
|
||||
name3: String,
|
||||
name4: String,
|
||||
name5: String,
|
||||
name6: String,
|
||||
name7: String,
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
||||
#[cfg(old)]
|
||||
fn blah() {
|
||||
// connect to the host server
|
||||
|
||||
let server_rendered = use_server_rendered((111111, 11111, 11), SERVER_RENDERED_KEY, || {
|
||||
ServerRendered {
|
||||
name0: "abc".to_string(),
|
||||
name1: "abc".to_string(),
|
||||
name2: "abc".to_string(),
|
||||
name3: "abc".to_string(),
|
||||
name4: "abc".to_string(),
|
||||
name5: "abc".to_string(),
|
||||
name6: "abc".to_string(),
|
||||
name7: "abc".to_string(),
|
||||
}
|
||||
});
|
||||
|
||||
let handler = dioxus_liveview::new_handler()
|
||||
.from_directory("abc123") // serve a given directory as the root
|
||||
.with_context(|| SomeContext {}) // build out a new context for all of the server-rendered components to share
|
||||
.with_route(SERVER_RENDERED_KEY, |cx: &ServerRendered| {
|
||||
//
|
||||
})
|
||||
.with_route(SERVER_RENDERED_KEY, |cx| {
|
||||
//
|
||||
})
|
||||
.with_route(SERVER_RENDERED_KEY, |cx| {
|
||||
//
|
||||
})
|
||||
.with_route(SERVER_RENDERED_KEY, |cx| {
|
||||
//
|
||||
})
|
||||
.with_route(SERVER_RENDERED_KEY, |cx| {
|
||||
//
|
||||
})
|
||||
.with_route(SERVER_RENDERED_KEY, |cx| {
|
||||
//
|
||||
})
|
||||
// depend on the framework, build a different type of handler
|
||||
// there are instructions on how to integrate this the various rusty web frameworks in the guide
|
||||
.build();
|
||||
|
||||
server.get("abc", handler);
|
||||
}
|
||||
|
||||
fn use_server_rendered<F: Properties>(_p: impl PartialEq, name: &'static str, f: impl Fn() -> F) {}
|
||||
@@ -0,0 +1,25 @@
|
||||
use dioxus_core as dioxus;
|
||||
use dioxus_core::prelude::*;
|
||||
|
||||
fn main() {
|
||||
dioxus_webview::launch(App, |f| f.with_maximized(true)).expect("Failed");
|
||||
}
|
||||
|
||||
static App: FC<()> = |cx| {
|
||||
//
|
||||
cx.render(rsx!(
|
||||
div {
|
||||
"hello world!"
|
||||
}
|
||||
))
|
||||
};
|
||||
|
||||
mod dioxus_elements {
|
||||
use super::*;
|
||||
pub struct div;
|
||||
impl DioxusElement for div {
|
||||
const TAG_NAME: &'static str = "div";
|
||||
const NAME_SPACE: Option<&'static str> = None;
|
||||
}
|
||||
pub trait GlobalAttributes {}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
//! webview dom
|
||||
|
||||
use dioxus_core::{DomEdit, RealDom, RealDomNode, ScopeId};
|
||||
use DomEdit::*;
|
||||
|
||||
pub struct WebviewRegistry {}
|
||||
|
||||
impl WebviewRegistry {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WebviewDom<'bump> {
|
||||
pub edits: Vec<DomEdit<'bump>>,
|
||||
pub node_counter: u64,
|
||||
pub registry: WebviewRegistry,
|
||||
}
|
||||
impl WebviewDom<'_> {
|
||||
pub fn new(registry: WebviewRegistry) -> Self {
|
||||
Self {
|
||||
edits: Vec::new(),
|
||||
node_counter: 0,
|
||||
registry,
|
||||
}
|
||||
}
|
||||
|
||||
// Finish using the dom (for its edit list) and give back the node and event registry
|
||||
pub fn consume(self) -> WebviewRegistry {
|
||||
self.registry
|
||||
}
|
||||
}
|
||||
impl<'bump> RealDom<'bump> for WebviewDom<'bump> {
|
||||
fn raw_node_as_any(&self) -> &mut dyn std::any::Any {
|
||||
todo!()
|
||||
// self.edits.push(PushRoot { root });
|
||||
}
|
||||
|
||||
fn request_available_node(&mut self) -> RealDomNode {
|
||||
self.node_counter += 1;
|
||||
RealDomNode::from_u64(self.node_counter)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
use std::fmt::{self, Write};
|
||||
|
||||
/// Escape a string to pass it into JavaScript.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use web_view::WebView;
|
||||
/// # use std::mem;
|
||||
/// #
|
||||
/// # let mut view: WebView<()> = unsafe { mem::uninitialized() };
|
||||
/// #
|
||||
/// let string = "Hello, world!";
|
||||
///
|
||||
/// // Calls the function callback with "Hello, world!" as its parameter.
|
||||
///
|
||||
/// view.eval(&format!("callback({});", web_view::escape(string)));
|
||||
/// ```
|
||||
pub fn escape(string: &str) -> Escaper {
|
||||
Escaper(string)
|
||||
}
|
||||
|
||||
// "All code points may appear literally in a string literal except for the
|
||||
// closing quote code points, U+005C (REVERSE SOLIDUS), U+000D (CARRIAGE
|
||||
// RETURN), U+2028 (LINE SEPARATOR), U+2029 (PARAGRAPH SEPARATOR), and U+000A
|
||||
// (LINE FEED)." - ES6 Specification
|
||||
|
||||
pub struct Escaper<'a>(&'a str);
|
||||
|
||||
const SPECIAL: &[char] = &[
|
||||
'\n', // U+000A (LINE FEED)
|
||||
'\r', // U+000D (CARRIAGE RETURN)
|
||||
'\'', // U+0027 (APOSTROPHE)
|
||||
'\\', // U+005C (REVERSE SOLIDUS)
|
||||
'\u{2028}', // U+2028 (LINE SEPARATOR)
|
||||
'\u{2029}', // U+2029 (PARAGRAPH SEPARATOR)
|
||||
];
|
||||
|
||||
impl<'a> fmt::Display for Escaper<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let &Escaper(mut string) = self;
|
||||
|
||||
f.write_char('\'')?;
|
||||
|
||||
while !string.is_empty() {
|
||||
if let Some(i) = string.find(SPECIAL) {
|
||||
if i > 0 {
|
||||
f.write_str(&string[..i])?;
|
||||
}
|
||||
|
||||
let mut chars = string[i..].chars();
|
||||
|
||||
f.write_str(match chars.next().unwrap() {
|
||||
'\n' => "\\n",
|
||||
'\r' => "\\r",
|
||||
'\'' => "\\'",
|
||||
'\\' => "\\\\",
|
||||
'\u{2028}' => "\\u2028",
|
||||
'\u{2029}' => "\\u2029",
|
||||
_ => unreachable!(),
|
||||
})?;
|
||||
|
||||
string = chars.as_str();
|
||||
} else {
|
||||
f.write_str(string)?;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
f.write_char('\'')?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let plain = "ABC \n\r' abc \\ \u{2028} \u{2029}123";
|
||||
let escaped = escape(plain).to_string();
|
||||
assert!(escaped == "'ABC \\n\\r\\' abc \\\\ \\u2028 \\u2029123'");
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<script>
|
||||
class Interpreter {
|
||||
constructor(root) {
|
||||
this.stack = [root];
|
||||
this.listeners = {};
|
||||
this.lastNodeWasText = false;
|
||||
this.nodes = {
|
||||
0: root
|
||||
};
|
||||
}
|
||||
|
||||
top() {
|
||||
return this.stack[this.stack.length - 1];
|
||||
}
|
||||
|
||||
pop() {
|
||||
return this.stack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
class OPTABLE {
|
||||
PushRoot(self, edit) {
|
||||
const id = edit.root;
|
||||
const node = self.nodes[id];
|
||||
self.stack.push(node);
|
||||
}
|
||||
AppendChildren(self, edit) {
|
||||
let root = self.stack[self.stack.length - (edit.many + 1)];
|
||||
for (let i = 0; i < edit.many; i++) {
|
||||
console.log("popping ", i, edit.many);
|
||||
let node = self.pop();
|
||||
root.appendChild(node);
|
||||
}
|
||||
}
|
||||
ReplaceWith(self, edit) {
|
||||
|
||||
let root = self.stack[self.stack.length - (edit.many + 1)];
|
||||
let els = [];
|
||||
|
||||
for (let i = 0; i < edit.many; i++) {
|
||||
els.push(self.pop());
|
||||
}
|
||||
|
||||
root.replaceWith(...els);
|
||||
}
|
||||
Remove(self, edit) {
|
||||
const node = self.stack.pop();
|
||||
node.remove();
|
||||
}
|
||||
RemoveAllChildren(self, edit) {
|
||||
}
|
||||
CreateTextNode(self, edit) {
|
||||
self.stack.push(document.createTextNode(edit.text));
|
||||
}
|
||||
CreateElement(self, edit) {
|
||||
const tagName = edit.tag;
|
||||
console.log(`creating element: `, edit);
|
||||
self.stack.push(document.createElement(tagName));
|
||||
}
|
||||
CreateElementNs(self, edit) {
|
||||
self.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"));
|
||||
}
|
||||
NewEventListener(self, edit) {
|
||||
}
|
||||
RemoveEventListener(self, edit) {
|
||||
}
|
||||
SetText(self, edit) {
|
||||
self.top().textContent = edit.text;
|
||||
}
|
||||
SetAttribute(self, edit) {
|
||||
const name = edit.field;
|
||||
const value = edit.value;
|
||||
const node = self.top(self.stack);
|
||||
node.setAttribute(name, value);
|
||||
|
||||
if ((name === "value", self)) {
|
||||
node.value = value;
|
||||
}
|
||||
if ((name === "checked", self)) {
|
||||
node.checked = true;
|
||||
}
|
||||
if ((name === "selected", self)) {
|
||||
node.selected = true;
|
||||
}
|
||||
}
|
||||
RemoveAttribute(self, edit) {
|
||||
const name = edit.field;
|
||||
const node = self.top(self.stack);
|
||||
node.removeAttribute(name);
|
||||
|
||||
if ((name === "value", self)) {
|
||||
node.value = null;
|
||||
}
|
||||
if ((name === "checked", self)) {
|
||||
node.checked = false;
|
||||
}
|
||||
if ((name === "selected", self)) {
|
||||
node.selected = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const op_table = new OPTABLE();
|
||||
|
||||
async function initialize() {
|
||||
const reply = await rpc.call('initiate');
|
||||
const interpreter = new Interpreter(window.document.getElementById("_dioxusroot"));
|
||||
console.log(reply);
|
||||
|
||||
for (let x = 0; x < reply.length; x++) {
|
||||
let edit = reply[x];
|
||||
console.log(edit);
|
||||
op_table[edit.type](interpreter, edit);
|
||||
}
|
||||
|
||||
console.log("stack completed: ", interpreter.stack);
|
||||
}
|
||||
console.log("initializing...");
|
||||
initialize();
|
||||
</script>
|
||||
</head>
|
||||
|
||||
|
||||
|
||||
<body>
|
||||
<div id="_dioxusroot">
|
||||
</div>
|
||||
</body>
|
||||
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,230 @@
|
||||
use std::borrow::BorrowMut;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::sync::mpsc::channel;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use dioxus_core::*;
|
||||
pub use wry;
|
||||
|
||||
use wry::application::event::{Event, WindowEvent};
|
||||
use wry::application::event_loop::{ControlFlow, EventLoop};
|
||||
use wry::application::window::Fullscreen;
|
||||
use wry::webview::WebViewBuilder;
|
||||
use wry::{
|
||||
application::window::{Window, WindowBuilder},
|
||||
webview::{RpcRequest, RpcResponse},
|
||||
};
|
||||
|
||||
mod dom;
|
||||
mod escape;
|
||||
|
||||
static HTML_CONTENT: &'static str = include_str!("./index.html");
|
||||
|
||||
pub fn launch(
|
||||
root: FC<()>,
|
||||
builder: impl FnOnce(WindowBuilder) -> WindowBuilder,
|
||||
) -> anyhow::Result<()> {
|
||||
launch_with_props(root, (), builder)
|
||||
}
|
||||
pub fn launch_with_props<P: Properties + 'static>(
|
||||
root: FC<P>,
|
||||
props: P,
|
||||
builder: impl FnOnce(WindowBuilder) -> WindowBuilder,
|
||||
) -> 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.
|
||||
/// Components used in WebviewRenderer instances can directly use system libraries, access the filesystem, and multithread with ease.
|
||||
pub struct WebviewRenderer<T> {
|
||||
/// The root component used to render the Webview
|
||||
root: FC<T>,
|
||||
}
|
||||
enum RpcEvent<'a> {
|
||||
Initialize {
|
||||
//
|
||||
edits: Vec<DomEdit<'a>>,
|
||||
},
|
||||
}
|
||||
|
||||
impl<T: Properties + 'static> WebviewRenderer<T> {
|
||||
pub fn run(
|
||||
root: FC<T>,
|
||||
props: T,
|
||||
user_builder: impl FnOnce(WindowBuilder) -> WindowBuilder,
|
||||
) -> anyhow::Result<()> {
|
||||
Self::run_with_edits(root, props, user_builder, None)
|
||||
}
|
||||
|
||||
pub fn run_with_edits(
|
||||
root: FC<T>,
|
||||
props: T,
|
||||
user_builder: impl FnOnce(WindowBuilder) -> WindowBuilder,
|
||||
redits: Option<Vec<DomEdit<'static>>>,
|
||||
) -> anyhow::Result<()> {
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
let window = user_builder(WindowBuilder::new()).build(&event_loop)?;
|
||||
|
||||
let vir = VirtualDom::new_with_props(root, props);
|
||||
|
||||
// todo: combine these or something
|
||||
let vdom = Arc::new(RwLock::new(vir));
|
||||
let registry = Arc::new(RwLock::new(Some(WebviewRegistry::new())));
|
||||
|
||||
let webview = WebViewBuilder::new(window)?
|
||||
// .with_visible(false)
|
||||
// .with_transparent(true)
|
||||
.with_url(&format!("data:text/html,{}", HTML_CONTENT))?
|
||||
.with_rpc_handler(move |_window: &Window, mut req: RpcRequest| {
|
||||
match req.method.as_str() {
|
||||
"initiate" => {
|
||||
let edits = if let Some(edits) = &redits {
|
||||
serde_json::to_value(edits).unwrap()
|
||||
} else {
|
||||
let mut lock = vdom.write().unwrap();
|
||||
let mut reg_lock = registry.write().unwrap();
|
||||
|
||||
// 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 edits = {
|
||||
let mut edits = Vec::new();
|
||||
lock.rebuild(&mut real, &mut edits).unwrap();
|
||||
serde_json::to_value(edits).unwrap()
|
||||
};
|
||||
|
||||
// Give back the registry into its slot
|
||||
*reg_lock = Some(real.consume());
|
||||
edits
|
||||
};
|
||||
|
||||
// Return the edits into the webview runtime
|
||||
Some(RpcResponse::new_result(req.id.take(), Some(edits)))
|
||||
}
|
||||
"user_event" => {
|
||||
let mut lock = vdom.write().unwrap();
|
||||
let mut reg_lock = registry.write().unwrap();
|
||||
|
||||
// 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 edits = {
|
||||
let mut edits = Vec::new();
|
||||
lock.rebuild(&mut real, &mut edits).unwrap();
|
||||
serde_json::to_value(edits).unwrap()
|
||||
};
|
||||
|
||||
// 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)))
|
||||
}
|
||||
_ => todo!("this message failed"),
|
||||
}
|
||||
})
|
||||
.build()?;
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Poll;
|
||||
|
||||
match event {
|
||||
Event::WindowEvent {
|
||||
event, window_id, ..
|
||||
} => match event {
|
||||
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
|
||||
WindowEvent::Resized(_) | WindowEvent::Moved(_) => {
|
||||
let _ = webview.resize();
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
Event::MainEventsCleared => {
|
||||
webview.resize();
|
||||
// window.request_redraw();
|
||||
}
|
||||
|
||||
_ => {} // Event::WindowEvent { event, .. } => {
|
||||
// //
|
||||
// match event {
|
||||
// WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
|
||||
// _ => {}
|
||||
// }
|
||||
// }
|
||||
// _ => {
|
||||
// // let _ = webview.resize();
|
||||
// }
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// 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>, builder: impl FnOnce() -> WVResult<WebView<'static, ()>>) -> Self {
|
||||
// Self { root }
|
||||
// }
|
||||
|
||||
/// Create a new text renderer from an existing Virtual DOM.
|
||||
/// This will progress the existing VDom's events to completion.
|
||||
pub fn from_vdom() -> Self {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Pass new args to the root function
|
||||
pub fn update(&mut self, _new_val: T) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Modify the root function in place, forcing a re-render regardless if the props changed
|
||||
pub fn update_mut(&mut self, _modifier: impl Fn(&mut T)) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::dom::WebviewRegistry;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct MessageParameters {
|
||||
message: String,
|
||||
}
|
||||
|
||||
fn HANDLER(window: &Window, mut req: RpcRequest) -> Option<RpcResponse> {
|
||||
let mut response = None;
|
||||
if &req.method == "fullscreen" {
|
||||
if let Some(params) = req.params.take() {
|
||||
if let Ok(mut args) = serde_json::from_value::<Vec<bool>>(params) {
|
||||
if !args.is_empty() {
|
||||
if args.swap_remove(0) {
|
||||
window.set_fullscreen(Some(Fullscreen::Borderless(None)));
|
||||
} else {
|
||||
window.set_fullscreen(None);
|
||||
}
|
||||
};
|
||||
response = Some(RpcResponse::new_result(req.id.take(), None));
|
||||
}
|
||||
}
|
||||
} else if &req.method == "send-parameters" {
|
||||
if let Some(params) = req.params.take() {
|
||||
if let Ok(mut args) = serde_json::from_value::<Vec<MessageParameters>>(params) {
|
||||
let result = if !args.is_empty() {
|
||||
let msg = args.swap_remove(0);
|
||||
Some(Value::String(format!("Hello, {}!", msg.message)))
|
||||
} else {
|
||||
// NOTE: in the real-world we should send an error response here!
|
||||
None
|
||||
};
|
||||
// Must always send a response as this is a `call()`
|
||||
response = Some(RpcResponse::new_result(req.id.take(), result));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
response
|
||||
}
|
||||
Reference in new issue
Block a user