Skip to content

Commit

Permalink
Merge pull request #243 from asaaki/update/wry-0-13
Browse files Browse the repository at this point in the history
Update to wry 0.13; support IPC, devtool, and feature flags
  • Loading branch information
jkelleyrtp authored Feb 15, 2022
2 parents 0d17da1 + 932ad01 commit 345dfd4
Show file tree
Hide file tree
Showing 11 changed files with 357 additions and 354 deletions.
19 changes: 13 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ web = ["dioxus-web"]
desktop = ["dioxus-desktop"]
router = ["dioxus-router"]

devtool = ["dioxus-desktop/devtool"]
fullscreen = ["dioxus-desktop/fullscreen"]
transparent = ["dioxus-desktop/transparent"]

tray = ["dioxus-desktop/tray"]
ayatana = ["dioxus-desktop/ayatana"]

# "dioxus-router/web"
# "dioxus-router/desktop"
# desktop = ["dioxus-desktop", "dioxus-router/desktop"]
Expand All @@ -57,15 +64,15 @@ members = [
]

[dev-dependencies]
futures-util = "0.3.17"
futures-util = "0.3.21"
log = "0.4.14"
num-format = "0.4.0"
separator = "0.4.1"
serde = { version = "1.0.131", features = ["derive"] }
serde = { version = "1.0.136", features = ["derive"] }
im-rc = "15.0.0"
anyhow = "1.0.51"
serde_json = "1.0.73"
anyhow = "1.0.53"
serde_json = "1.0.79"
rand = { version = "0.8.4", features = ["small_rng"] }
tokio = { version = "1.14.0", features = ["full"] }
reqwest = { version = "0.11.8", features = ["json"] }
tokio = { version = "1.16.1", features = ["full"] }
reqwest = { version = "0.11.9", features = ["json"] }
dioxus = { path = ".", features = ["desktop", "ssr", "router"] }
23 changes: 15 additions & 8 deletions packages/desktop/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ keywords = ["dom", "ui", "gui", "react", "wasm"]

[dependencies]
dioxus-core = { path = "../core", version = "^0.1.9", features = ["serialize"] }
argh = "0.1.4"
serde = "1.0.120"
serde_json = "1.0.61"
thiserror = "1.0.23"
log = "0.4.13"
argh = "0.1.7"
serde = "1.0.136"
serde_json = "1.0.79"
thiserror = "1.0.30"
log = "0.4.14"
html-escape = "0.2.9"
wry = "0.12.2"
futures-channel = "0.3"
tokio = { version = "1.12.0", features = [
wry = { version = "0.13.1" }
futures-channel = "0.3.21"
tokio = { version = "1.16.1", features = [
"sync",
"rt-multi-thread",
"rt",
Expand All @@ -38,6 +38,13 @@ dioxus-interpreter-js = { path = "../interpreter", version = "^0.0.0" }
default = ["tokio_runtime"]
tokio_runtime = ["tokio"]

devtool = ["wry/devtool"]
fullscreen = ["wry/fullscreen"]
transparent = ["wry/transparent"]

tray = ["wry/tray"]
ayatana = ["wry/ayatana"]


[dev-dependencies]
dioxus-hooks = { path = "../hooks" }
Expand Down
8 changes: 4 additions & 4 deletions packages/desktop/src/cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ use wry::{
pub(crate) type DynEventHandlerFn = dyn Fn(&mut EventLoop<()>, &mut WebView);

pub struct DesktopConfig {
pub window: WindowBuilder,
pub file_drop_handler: Option<Box<dyn Fn(&Window, FileDropEvent) -> bool>>,
pub protocols: Vec<WryProtocol>,
pub(crate) window: WindowBuilder,
pub(crate) file_drop_handler: Option<Box<dyn Fn(&Window, FileDropEvent) -> bool>>,
pub(crate) protocols: Vec<WryProtocol>,
pub(crate) pre_rendered: Option<String>,
pub(crate) event_handler: Option<Box<DynEventHandlerFn>>,
}

pub type WryProtocol = (
pub(crate) type WryProtocol = (
String,
Box<dyn Fn(&HttpRequest) -> WryResult<HttpResponse> + 'static>,
);
Expand Down
106 changes: 106 additions & 0 deletions packages/desktop/src/controller.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
use crate::desktop_context::DesktopContext;
use crate::user_window_events::UserWindowEvent;
use dioxus_core::*;
use std::{
collections::{HashMap, VecDeque},
sync::atomic::AtomicBool,
sync::{Arc, RwLock},
};
use wry::{
self,
application::{event_loop::ControlFlow, event_loop::EventLoopProxy, window::WindowId},
webview::WebView,
};

pub(super) struct DesktopController {
pub(super) webviews: HashMap<WindowId, WebView>,
pub(super) sender: futures_channel::mpsc::UnboundedSender<SchedulerMsg>,
pub(super) pending_edits: Arc<RwLock<VecDeque<String>>>,
pub(super) quit_app_on_close: bool,
pub(super) is_ready: Arc<AtomicBool>,
}

impl DesktopController {
// Launch the virtualdom on its own thread managed by tokio
// returns the desktop state
pub(super) fn new_on_tokio<P: Send + 'static>(
root: Component<P>,
props: P,
proxy: EventLoopProxy<UserWindowEvent>,
) -> Self {
let edit_queue = Arc::new(RwLock::new(VecDeque::new()));
let pending_edits = edit_queue.clone();

let (sender, receiver) = futures_channel::mpsc::unbounded::<SchedulerMsg>();
let return_sender = sender.clone();

let desktop_context_proxy = proxy.clone();
std::thread::spawn(move || {
// We create the runtime as multithreaded, so you can still "spawn" onto multiple threads
let runtime = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap();

runtime.block_on(async move {
let mut dom =
VirtualDom::new_with_props_and_scheduler(root, props, (sender, receiver));

let window_context = DesktopContext::new(desktop_context_proxy);

dom.base_scope().provide_context(window_context);

let edits = dom.rebuild();

edit_queue
.write()
.unwrap()
.push_front(serde_json::to_string(&edits.edits).unwrap());

loop {
dom.wait_for_work().await;
let mut muts = dom.work_with_deadline(|| false);

while let Some(edit) = muts.pop() {
edit_queue
.write()
.unwrap()
.push_front(serde_json::to_string(&edit.edits).unwrap());
}

let _ = proxy.send_event(UserWindowEvent::Update);
}
})
});

Self {
pending_edits,
sender: return_sender,
webviews: HashMap::new(),
is_ready: Arc::new(AtomicBool::new(false)),
quit_app_on_close: true,
}
}

pub(super) fn close_window(&mut self, window_id: WindowId, control_flow: &mut ControlFlow) {
self.webviews.remove(&window_id);

if self.webviews.is_empty() && self.quit_app_on_close {
*control_flow = ControlFlow::Exit;
}
}

pub(super) fn try_load_ready_webviews(&mut self) {
if self.is_ready.load(std::sync::atomic::Ordering::Relaxed) {
let mut queue = self.pending_edits.write().unwrap();
let (_id, view) = self.webviews.iter_mut().next().unwrap();

while let Some(edit) = queue.pop_back() {
view.evaluate_script(&format!("window.interpreter.handleEdits({})", edit))
.unwrap();
}
} else {
println!("waiting for ready");
}
}
}
47 changes: 25 additions & 22 deletions packages/desktop/src/desktop_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ use std::rc::Rc;
use dioxus_core::ScopeState;
use wry::application::event_loop::EventLoopProxy;

use crate::UserWindowEvent;
use crate::user_window_events::UserWindowEvent;
use UserWindowEvent::*;

type ProxyType = EventLoopProxy<UserWindowEvent>;

Expand Down Expand Up @@ -35,75 +36,77 @@ impl DesktopContext {
/// onmousedown: move |_| { desktop.drag_window(); }
/// ```
pub fn drag(&self) {
let _ = self.proxy.send_event(UserWindowEvent::DragWindow);
let _ = self.proxy.send_event(DragWindow);
}

/// set window minimize state
pub fn set_minimized(&self, minimized: bool) {
let _ = self.proxy.send_event(UserWindowEvent::Minimize(minimized));
let _ = self.proxy.send_event(Minimize(minimized));
}

/// set window maximize state
pub fn set_maximized(&self, maximized: bool) {
let _ = self.proxy.send_event(UserWindowEvent::Maximize(maximized));
let _ = self.proxy.send_event(Maximize(maximized));
}

/// toggle window maximize state
pub fn toggle_maximized(&self) {
let _ = self.proxy.send_event(MaximizeToggle);
}

/// set window visible or not
pub fn set_visible(&self, visible: bool) {
let _ = self.proxy.send_event(UserWindowEvent::Visible(visible));
let _ = self.proxy.send_event(Visible(visible));
}

/// close window
pub fn close(&self) {
let _ = self.proxy.send_event(UserWindowEvent::CloseWindow);
let _ = self.proxy.send_event(CloseWindow);
}

/// set window to focus
pub fn focus(&self) {
let _ = self.proxy.send_event(UserWindowEvent::FocusWindow);
let _ = self.proxy.send_event(FocusWindow);
}

/// change window to fullscreen
pub fn set_fullscreen(&self, fullscreen: bool) {
let _ = self
.proxy
.send_event(UserWindowEvent::Fullscreen(fullscreen));
let _ = self.proxy.send_event(Fullscreen(fullscreen));
}

/// set resizable state
pub fn set_resizable(&self, resizable: bool) {
let _ = self.proxy.send_event(UserWindowEvent::Resizable(resizable));
let _ = self.proxy.send_event(Resizable(resizable));
}

/// set the window always on top
pub fn set_always_on_top(&self, top: bool) {
let _ = self.proxy.send_event(UserWindowEvent::AlwaysOnTop(top));
let _ = self.proxy.send_event(AlwaysOnTop(top));
}

// set cursor visible or not
pub fn set_cursor_visible(&self, visible: bool) {
let _ = self
.proxy
.send_event(UserWindowEvent::CursorVisible(visible));
let _ = self.proxy.send_event(CursorVisible(visible));
}

// set cursor grab
pub fn set_cursor_grab(&self, grab: bool) {
let _ = self.proxy.send_event(UserWindowEvent::CursorGrab(grab));
let _ = self.proxy.send_event(CursorGrab(grab));
}

/// set window title
pub fn set_title(&self, title: &str) {
let _ = self
.proxy
.send_event(UserWindowEvent::SetTitle(String::from(title)));
let _ = self.proxy.send_event(SetTitle(String::from(title)));
}

/// change window to borderless
pub fn set_decorations(&self, decoration: bool) {
let _ = self
.proxy
.send_event(UserWindowEvent::SetDecorations(decoration));
let _ = self.proxy.send_event(SetDecorations(decoration));
}

/// opens DevTool window
pub fn devtool(&self) {
let _ = self.proxy.send_event(DevTool);
}
}

Expand Down
40 changes: 31 additions & 9 deletions packages/desktop/src/events.rs
Original file line number Diff line number Diff line change
@@ -1,33 +1,55 @@
//! Convert a serialized event to an event Trigger
//!
//! Convert a serialized event to an event trigger
use std::any::Any;
use std::sync::Arc;

use dioxus_core::{ElementId, EventPriority, UserEvent};
use dioxus_html::on::*;

#[derive(serde::Serialize, serde::Deserialize)]
pub(crate) struct IpcMessage {
method: String,
params: serde_json::Value,
}

impl IpcMessage {
pub(crate) fn method(&self) -> &str {
self.method.as_str()
}

pub(crate) fn params(self) -> serde_json::Value {
self.params
}
}

pub(crate) fn parse_ipc_message(payload: &str) -> Option<IpcMessage> {
let mm = serde_json::from_str(payload);
match mm {
Ok(message) => Some(message),
Err(e) => {
log::error!("could not parse IPC message, error: {e}");
None
}
}
}

#[derive(serde::Serialize, serde::Deserialize)]
struct ImEvent {
event: String,
mounted_dom_id: u64,
// scope: u64,
contents: serde_json::Value,
}

pub fn trigger_from_serialized(val: serde_json::Value) -> UserEvent {
let ims: Vec<ImEvent> = serde_json::from_value(val).unwrap();

let ImEvent {
event,
mounted_dom_id,
contents,
} = ims.into_iter().next().unwrap();
} = serde_json::from_value(val).unwrap();

// let scope_id = ScopeId(scope as usize);
let mounted_dom_id = Some(ElementId(mounted_dom_id as usize));

let name = event_name_from_typ(&event);
let name = event_name_from_type(&event);
let event = make_synthetic_event(&event, contents);

UserEvent {
Expand Down Expand Up @@ -105,7 +127,7 @@ fn make_synthetic_event(name: &str, val: serde_json::Value) -> Arc<dyn Any + Sen
}
}

fn event_name_from_typ(typ: &str) -> &'static str {
fn event_name_from_type(typ: &str) -> &'static str {
match typ {
"copy" => "copy",
"cut" => "cut",
Expand Down
Loading

0 comments on commit 345dfd4

Please sign in to comment.