mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-26 22:20:19 +00:00
feat: move webview to wry
This commit is contained in:
parent
2547da36a0
commit
99d94b69ab
11 changed files with 419 additions and 96 deletions
10
README.md
10
README.md
|
@ -8,13 +8,13 @@
|
|||
Dioxus is a portable, performant, and ergonomic framework for building cross-platform user experiences in Rust.
|
||||
|
||||
```rust
|
||||
fn Example(cx: Context<()>) -> VNode {
|
||||
let name = use_state(cx, || "..?");
|
||||
fn App(cx: Context<()>) -> VNode {
|
||||
let mut count = use_state(cx, || 0);
|
||||
|
||||
cx.render(rsx! {
|
||||
h1 { "Hello, {name}" }
|
||||
button { "?", onclick: move |_| name.set("world!")}
|
||||
button { "?", onclick: move |_| name.set("Dioxus 🎉")}
|
||||
h1 { "Hi-Five counter: {count}" }
|
||||
button { onclick: move |_| count += 1, "Up high!" }
|
||||
button { onclick: move |_| count -= 1, "Down low!" }
|
||||
})
|
||||
};
|
||||
```
|
||||
|
|
|
@ -21,8 +21,11 @@ use dioxus::prelude::*;
|
|||
const STYLE: &str = include_str!("./assets/calculator.css");
|
||||
fn main() {
|
||||
dioxus::desktop::launch(App, |cfg| {
|
||||
cfg.title("Calculator Demo").resizable(false).size(350, 550)
|
||||
});
|
||||
cfg.with_title("Calculator Demo")
|
||||
.with_resizable(true)
|
||||
.with_skip_taskbar(true)
|
||||
})
|
||||
.expect("failed to launch dioxus app");
|
||||
}
|
||||
|
||||
enum Operator {
|
||||
|
|
|
@ -20,12 +20,9 @@ static App: FC<()> = |cx| {
|
|||
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
h1 { "Dioxus Desktop Demo" }
|
||||
p { "Count is {count}" }
|
||||
button {
|
||||
"Click to increment"
|
||||
onclick: move |_| count += 1
|
||||
}
|
||||
h1 { "Hifive counter: {count}" }
|
||||
button { onclick: move |_| count += 1, "Up high!" }
|
||||
button { onclick: move |_| count -= 1, "Down low!" }
|
||||
}
|
||||
})
|
||||
};
|
||||
|
|
|
@ -102,8 +102,11 @@ where
|
|||
impl<'a, T: 'static> UseState<'a, T> {
|
||||
/// Tell the Dioxus Scheduler that we need to be processed
|
||||
pub fn needs_update(&self) {
|
||||
if !self.inner.update_scheuled.get() {
|
||||
self.inner.update_scheuled.set(true);
|
||||
(self.inner.callback)();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set(&self, new_val: T) {
|
||||
self.needs_update();
|
||||
|
@ -143,7 +146,7 @@ impl<'a, T: 'static> std::ops::Deref for UseState<'a, T> {
|
|||
}
|
||||
}
|
||||
|
||||
use std::ops::{Add, AddAssign};
|
||||
use std::ops::{Add, AddAssign, Sub, SubAssign};
|
||||
impl<'a, T: Copy + Add<T, Output = T>> Add<T> for UseState<'a, T> {
|
||||
type Output = T;
|
||||
|
||||
|
@ -156,6 +159,18 @@ impl<'a, T: Copy + Add<T, Output = T>> AddAssign<T> for UseState<'a, T> {
|
|||
self.set(self.inner.current_val.add(rhs));
|
||||
}
|
||||
}
|
||||
impl<'a, T: Copy + Sub<T, Output = T>> Sub<T> for UseState<'a, T> {
|
||||
type Output = T;
|
||||
|
||||
fn sub(self, rhs: T) -> Self::Output {
|
||||
self.inner.current_val.sub(rhs)
|
||||
}
|
||||
}
|
||||
impl<'a, T: Copy + Sub<T, Output = T>> SubAssign<T> for UseState<'a, T> {
|
||||
fn sub_assign(&mut self, rhs: T) {
|
||||
self.set(self.inner.current_val.sub(rhs));
|
||||
}
|
||||
}
|
||||
|
||||
// enable displaty for the handle
|
||||
impl<'a, T: 'static + Display> std::fmt::Display for UseState<'a, T> {
|
||||
|
@ -165,6 +180,7 @@ impl<'a, T: 'static + Display> std::fmt::Display for UseState<'a, T> {
|
|||
}
|
||||
struct UseStateInner<T: 'static> {
|
||||
current_val: T,
|
||||
update_scheuled: Cell<bool>,
|
||||
callback: Rc<dyn Fn()>,
|
||||
wip: RefCell<Option<T>>,
|
||||
}
|
||||
|
@ -213,9 +229,10 @@ pub fn use_state<'a, 'c, T: 'static, F: FnOnce() -> T, P>(
|
|||
current_val: initial_state_fn(),
|
||||
callback: cx.schedule_update(),
|
||||
wip: RefCell::new(None),
|
||||
update_scheuled: Cell::new(false),
|
||||
},
|
||||
move |hook| {
|
||||
log::debug!("addr of hook: {:#?}", hook as *const _);
|
||||
hook.update_scheuled.set(false);
|
||||
let mut new_val = hook.wip.borrow_mut();
|
||||
if new_val.is_some() {
|
||||
hook.current_val = new_val.take().unwrap();
|
||||
|
|
|
@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize};
|
|||
/// network or through FFI boundaries.
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum DomEdits<'bump> {
|
||||
pub enum DomEdit<'bump> {
|
||||
PushRoot {
|
||||
root: u64,
|
||||
},
|
||||
|
|
23
packages/webview/examples/test.rs
Normal file
23
packages/webview/examples/test.rs
Normal file
|
@ -0,0 +1,23 @@
|
|||
fn main() {
|
||||
dioxus_webview::launch(App, |f| f.with_focus().with_maximized(true)).expect("Failed");
|
||||
}
|
||||
|
||||
static App: FC<()> = |cx| {
|
||||
//
|
||||
cx.render(rsx!(
|
||||
div {
|
||||
"hello world!"
|
||||
}
|
||||
))
|
||||
};
|
||||
|
||||
use dioxus_core as dioxus;
|
||||
use dioxus_core::prelude::*;
|
||||
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;
|
||||
}
|
||||
}
|
99
packages/webview/src/blah.html
Normal file
99
packages/webview/src/blah.html
Normal file
|
@ -0,0 +1,99 @@
|
|||
class OPTABLE {
|
||||
PushRoot(self, edit) {
|
||||
const id = edit.root;
|
||||
const node = self.nodes[id];
|
||||
self.stack.push(node);
|
||||
}
|
||||
AppendChild(self, edit) {
|
||||
// todo: prevent merging of text nodes
|
||||
const node = self.pop();
|
||||
self.top().appendChild(node);
|
||||
}
|
||||
ReplaceWith(self, edit) {
|
||||
const newNode = self.pop();
|
||||
const oldNode = self.pop();
|
||||
oldNode.replaceWith(newNode);
|
||||
self.stack.push(newNode);
|
||||
}
|
||||
Remove(self, edit) {
|
||||
const node = self.stack.pop();
|
||||
node.remove();
|
||||
}
|
||||
RemoveAllChildren(self, edit) {
|
||||
// todo - we never actually call this one
|
||||
}
|
||||
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) {
|
||||
self.stack.push(document.createElement("pre"));
|
||||
}
|
||||
NewEventListener(self, edit) {
|
||||
// todo
|
||||
}
|
||||
RemoveEventListener(self, edit) {
|
||||
// todo
|
||||
}
|
||||
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);
|
||||
|
||||
// Some attributes are "volatile" and don't work through `setAttribute`.
|
||||
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);
|
||||
|
||||
// Some attributes are "volatile" and don't work through `removeAttribute`.
|
||||
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();
|
||||
// const interpreter = new Interpreter(window.document.body);
|
||||
|
||||
// function EditListReceived(rawEditList) {
|
||||
// let editList = JSON.parse(rawEditList);
|
||||
// console.warn("hnelllo");
|
||||
// editList.forEach(function (edit, index) {
|
||||
// console.log(edit);
|
||||
// op_table[edit.type](interpreter, edit);
|
||||
// });
|
||||
// }
|
||||
|
||||
// async function rinalize() {
|
||||
// console.log("initialize...");
|
||||
// let edits = await rpc.call("initiate");
|
||||
// console.error(edits);
|
||||
// }
|
81
packages/webview/src/blah.js
Normal file
81
packages/webview/src/blah.js
Normal file
|
@ -0,0 +1,81 @@
|
|||
class OPTABLE {
|
||||
PushRoot(self, edit) {
|
||||
const id = edit.root;
|
||||
const node = self.nodes[id];
|
||||
self.stack.push(node);
|
||||
}
|
||||
AppendChild(self, edit) {
|
||||
// todo: prevent merging of text nodes
|
||||
const node = self.pop();
|
||||
self.top().appendChild(node);
|
||||
}
|
||||
ReplaceWith(self, edit) {
|
||||
const newNode = self.pop();
|
||||
const oldNode = self.pop();
|
||||
oldNode.replaceWith(newNode);
|
||||
self.stack.push(newNode);
|
||||
}
|
||||
Remove(self, edit) {
|
||||
const node = self.stack.pop();
|
||||
node.remove();
|
||||
}
|
||||
RemoveAllChildren(self, edit) {
|
||||
// todo - we never actually call this one
|
||||
}
|
||||
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) {
|
||||
self.stack.push(document.createElement("pre"));
|
||||
}
|
||||
NewEventListener(self, edit) {
|
||||
// todo
|
||||
}
|
||||
RemoveEventListener(self, edit) {
|
||||
// todo
|
||||
}
|
||||
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);
|
||||
|
||||
// Some attributes are "volatile" and don't work through `setAttribute`.
|
||||
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);
|
||||
|
||||
// Some attributes are "volatile" and don't work through `removeAttribute`.
|
||||
if ((name === "value", self)) {
|
||||
node.value = null;
|
||||
}
|
||||
if ((name === "checked", self)) {
|
||||
node.checked = false;
|
||||
}
|
||||
if ((name === "selected", self)) {
|
||||
node.selected = false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,13 +4,13 @@ use dioxus_core as dioxus;
|
|||
use dioxus_core::prelude::*;
|
||||
use dioxus_core::{
|
||||
diff::RealDom,
|
||||
serialize::DomEdits,
|
||||
serialize::DomEdit,
|
||||
virtual_dom::{RealDomNode, VirtualDom},
|
||||
};
|
||||
use DomEdits::*;
|
||||
use DomEdit::*;
|
||||
|
||||
pub struct WebviewDom<'bump> {
|
||||
pub edits: Vec<DomEdits<'bump>>,
|
||||
pub edits: Vec<DomEdit<'bump>>,
|
||||
pub node_counter: u64,
|
||||
}
|
||||
impl WebviewDom<'_> {
|
||||
|
|
|
@ -1,20 +1,22 @@
|
|||
<!-- a js-only interpreter for the dioxus patch stream :) -->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta content="text/html;charset=utf-8" http-equiv="Content-Type" />
|
||||
<meta charset="UTF-8" />
|
||||
<link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet" />
|
||||
<!-- <link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet" /> -->
|
||||
</head>
|
||||
|
||||
|
||||
|
||||
|
||||
<body>
|
||||
<div></div>
|
||||
</body>
|
||||
<script>
|
||||
|
||||
class Interpreter {
|
||||
constructor(root) {
|
||||
this.stack = [root];
|
||||
this.listeners = {};
|
||||
this.lastNodeWasText = false;
|
||||
this.nodes = {
|
||||
0: root
|
||||
|
@ -30,6 +32,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
class OPTABLE {
|
||||
PushRoot(self, edit) {
|
||||
const id = edit.root;
|
||||
|
@ -37,7 +40,6 @@
|
|||
self.stack.push(node);
|
||||
}
|
||||
AppendChild(self, edit) {
|
||||
// todo: prevent merging of text nodes
|
||||
const node = self.pop();
|
||||
self.top().appendChild(node);
|
||||
}
|
||||
|
@ -52,14 +54,13 @@
|
|||
node.remove();
|
||||
}
|
||||
RemoveAllChildren(self, edit) {
|
||||
// todo - we never actually call this one
|
||||
}
|
||||
CreateTextNode(self, edit) {
|
||||
self.stack.push(document.createTextNode(edit.text));
|
||||
}
|
||||
CreateElement(self, edit) {
|
||||
const tagName = edit.tag;
|
||||
console.log(`creating element! ${edit}`);
|
||||
console.log(`creating element: `, edit);
|
||||
self.stack.push(document.createElement(tagName));
|
||||
}
|
||||
CreateElementNs(self, edit) {
|
||||
|
@ -69,10 +70,8 @@
|
|||
self.stack.push(document.createElement("pre"));
|
||||
}
|
||||
NewEventListener(self, edit) {
|
||||
// todo
|
||||
}
|
||||
RemoveEventListener(self, edit) {
|
||||
// todo
|
||||
}
|
||||
SetText(self, edit) {
|
||||
self.top().textContent = edit.text;
|
||||
|
@ -83,7 +82,6 @@
|
|||
const node = self.top(self.stack);
|
||||
node.setAttribute(name, value);
|
||||
|
||||
// Some attributes are "volatile" and don't work through `setAttribute`.
|
||||
if ((name === "value", self)) {
|
||||
node.value = value;
|
||||
}
|
||||
|
@ -99,7 +97,6 @@
|
|||
const node = self.top(self.stack);
|
||||
node.removeAttribute(name);
|
||||
|
||||
// Some attributes are "volatile" and don't work through `removeAttribute`.
|
||||
if ((name === "value", self)) {
|
||||
node.value = null;
|
||||
}
|
||||
|
@ -110,11 +107,9 @@
|
|||
node.selected = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const op_table = new OPTABLE();
|
||||
const interpreter = new Interpreter(window.document.body);
|
||||
|
||||
|
||||
function EditListReceived(rawEditList) {
|
||||
let editList = JSON.parse(rawEditList);
|
||||
|
@ -125,7 +120,20 @@
|
|||
});
|
||||
}
|
||||
|
||||
external.invoke("initiate");
|
||||
const op_table = new OPTABLE();
|
||||
const interpreter = new Interpreter(window.document.body);
|
||||
|
||||
async function initialize() {
|
||||
const reply = await rpc.call('initiate');
|
||||
console.log(reply);
|
||||
reply.forEach(function (edit, index) {
|
||||
console.log(edit);
|
||||
op_table[edit.type](interpreter, edit);
|
||||
});
|
||||
}
|
||||
console.log("initializing...");
|
||||
initialize();
|
||||
</script>
|
||||
|
||||
|
||||
</html>
|
||||
|
|
|
@ -1,24 +1,28 @@
|
|||
use std::borrow::BorrowMut;
|
||||
use std::sync::mpsc::channel;
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use dioxus_core::prelude::*;
|
||||
use dioxus_core::virtual_dom::VirtualDom;
|
||||
use web_view::{escape, Handle};
|
||||
use web_view::{WVResult, WebView, WebViewBuilder};
|
||||
use dioxus_core::{prelude::*, serialize::DomEdit};
|
||||
use wry::{
|
||||
application::window::{Window, WindowBuilder},
|
||||
webview::{RpcRequest, RpcResponse},
|
||||
};
|
||||
|
||||
mod dom;
|
||||
|
||||
static HTML_CONTENT: &'static str = include_str!("./index.html");
|
||||
|
||||
pub fn launch(
|
||||
root: FC<()>,
|
||||
builder: impl FnOnce(DioxusWebviewBuilder) -> DioxusWebviewBuilder,
|
||||
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(DioxusWebviewBuilder) -> DioxusWebviewBuilder,
|
||||
builder: impl FnOnce(WindowBuilder) -> WindowBuilder,
|
||||
) -> anyhow::Result<()> {
|
||||
WebviewRenderer::run(root, props, builder)
|
||||
}
|
||||
|
@ -29,85 +33,125 @@ pub struct WebviewRenderer<T> {
|
|||
/// The root component used to render the Webview
|
||||
root: FC<T>,
|
||||
}
|
||||
|
||||
enum InnerEvent {
|
||||
Initiate(Handle<()>),
|
||||
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(DioxusWebviewBuilder) -> DioxusWebviewBuilder,
|
||||
user_builder: impl FnOnce(WindowBuilder) -> WindowBuilder,
|
||||
) -> anyhow::Result<()> {
|
||||
let (sender, receiver) = channel::<InnerEvent>();
|
||||
use wry::{
|
||||
application::{
|
||||
event::{Event, StartCause, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::WindowBuilder,
|
||||
},
|
||||
webview::WebViewBuilder,
|
||||
};
|
||||
|
||||
let DioxusWebviewBuilder {
|
||||
title,
|
||||
width,
|
||||
height,
|
||||
resizable,
|
||||
debug,
|
||||
frameless,
|
||||
visible,
|
||||
min_width,
|
||||
min_height,
|
||||
} = user_builder(DioxusWebviewBuilder::new());
|
||||
let event_loop = EventLoop::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 window = user_builder(WindowBuilder::new()).build(&event_loop)?;
|
||||
|
||||
let mut vdom = VirtualDom::new_with_props(root, props);
|
||||
let mut real_dom = dom::WebviewDom::new();
|
||||
vdom.rebuild(&mut real_dom)?;
|
||||
|
||||
let ref_edits = Arc::new(serde_json::to_string(&real_dom.edits)?);
|
||||
let edits = Arc::new(RwLock::new(Some(serde_json::to_value(real_dom.edits)?)));
|
||||
|
||||
loop {
|
||||
view.step()
|
||||
.expect("should not fail")
|
||||
.expect("should not fail");
|
||||
std::thread::sleep(std::time::Duration::from_millis(15));
|
||||
// let ref_edits = Arc::new(serde_json::to_string(&real_dom.edits)?);
|
||||
|
||||
if let Ok(event) = receiver.try_recv() {
|
||||
if let InnerEvent::Initiate(handle) = event {
|
||||
let editlist = ref_edits.clone();
|
||||
handle
|
||||
.dispatch(move |view| {
|
||||
let escaped = escape(&editlist);
|
||||
view.eval(&format!("EditListReceived({});", escaped))
|
||||
})
|
||||
.expect("Dispatch failed");
|
||||
let handler = move |window: &Window, mut req: RpcRequest| {
|
||||
//
|
||||
let d = edits.clone();
|
||||
match req.method.as_str() {
|
||||
"initiate" => {
|
||||
let mut ed = d.write().unwrap();
|
||||
let edits = match ed.as_mut() {
|
||||
Some(ed) => Some(ed.take()),
|
||||
None => None,
|
||||
};
|
||||
Some(RpcResponse::new_result(req.id.take(), edits))
|
||||
}
|
||||
_ => todo!("this message failed"),
|
||||
}
|
||||
};
|
||||
|
||||
let webview = WebViewBuilder::new(window)?
|
||||
.with_url(&format!("data:text/html,{}", HTML_CONTENT))?
|
||||
.with_rpc_handler(handler)
|
||||
.build()?;
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
|
||||
match event {
|
||||
Event::WindowEvent { event, .. } => {
|
||||
//
|
||||
match event {
|
||||
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// let _ = webview.resize();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 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();
|
||||
// loop {
|
||||
// view.step()
|
||||
// .expect("should not fail")
|
||||
// .expect("should not fail");
|
||||
// std::thread::sleep(std::time::Duration::from_millis(15));
|
||||
|
||||
// if let Ok(event) = receiver.try_recv() {
|
||||
// if let InnerEvent::Initiate(handle) = event {
|
||||
// let editlist = ref_edits.clone();
|
||||
// handle
|
||||
// .dispatch(move |view| {
|
||||
// let escaped = escape(&editlist);
|
||||
// view.eval(&format!("EditListReceived({});", escaped))
|
||||
// })
|
||||
// .expect("Dispatch failed");
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
/// 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 }
|
||||
}
|
||||
// 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.
|
||||
|
@ -126,6 +170,57 @@ impl<T: Properties + 'static> WebviewRenderer<T> {
|
|||
}
|
||||
}
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct MessageParameters {
|
||||
message: String,
|
||||
}
|
||||
|
||||
fn HANDLER(window: &Window, mut req: RpcRequest) -> Option<RpcResponse> {
|
||||
use wry::{
|
||||
application::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::{Fullscreen, Window, WindowBuilder},
|
||||
},
|
||||
webview::{RpcRequest, RpcResponse, WebViewBuilder},
|
||||
};
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
pub struct DioxusWebviewBuilder<'a> {
|
||||
pub(crate) title: &'a str,
|
||||
pub(crate) width: i32,
|
||||
|
|
Loading…
Reference in a new issue