change to hook api

This commit is contained in:
Evan Almloff committed 2023-01-12 18:48:23 -06:00
1 parent 2772b86629
commit cd17f515d4
5 files changed
+187 -26

No files matched your search

+42
View File
@@ -0,0 +1,42 @@
use dioxus::prelude::*;
use dioxus_desktop::tao::event::WindowEvent;
use dioxus_desktop::use_wry_event_handler;
use dioxus_desktop::wry::application::event::Event as WryEvent;
fn main() {
dioxus_desktop::launch(app);
}
fn app(cx: Scope) -> Element {
let focused = use_state(cx, || false);
use_wry_event_handler(cx, {
to_owned![focused];
move |event, _| {
if let WryEvent::WindowEvent {
event: WindowEvent::Focused(new_focused),
..
} = event
{
focused.set(*new_focused);
}
}
});
cx.render(rsx! {
div{
width: "100%",
height: "100%",
display: "flex",
flex_direction: "column",
align_items: "center",
{
if *focused.get() {
"This window is focused!"
} else {
"This window is not focused!"
}
}
}
})
}
+1
View File
@@ -32,6 +32,7 @@ tokio = { version = "1.16.1", features = [
webbrowser = "0.8.0" webbrowser = "0.8.0"
infer = "0.11.0" infer = "0.11.0"
dunce = "1.0.2" dunce = "1.0.2"
slab = "0.4"
interprocess = { version = "1.1.1", optional = true } interprocess = { version = "1.1.1", optional = true }
futures-util = "0.3.25" futures-util = "0.3.25"
-18
View File
@@ -1,7 +1,5 @@
use std::path::PathBuf; use std::path::PathBuf;
use wry::application::event::Event;
use wry::application::event_loop::EventLoopWindowTarget;
use wry::application::window::Icon; use wry::application::window::Icon;
use wry::{ use wry::{
application::window::{Window, WindowBuilder}, application::window::{Window, WindowBuilder},
@@ -10,18 +8,14 @@ use wry::{
Result as WryResult, Result as WryResult,
}; };
use crate::desktop_context::UserWindowEvent;
// pub(crate) type DynEventHandlerFn = dyn Fn(&mut EventLoop<()>, &mut WebView); // pub(crate) type DynEventHandlerFn = dyn Fn(&mut EventLoop<()>, &mut WebView);
/// The configuration for the desktop application. /// The configuration for the desktop application.
pub struct Config { pub struct Config {
pub(crate) window: WindowBuilder, pub(crate) window: WindowBuilder,
pub(crate) file_drop_handler: Option<DropHandler>, pub(crate) file_drop_handler: Option<DropHandler>,
pub(crate) event_handler: Option<EventHandler>,
pub(crate) protocols: Vec<WryProtocol>, pub(crate) protocols: Vec<WryProtocol>,
pub(crate) pre_rendered: Option<String>, pub(crate) pre_rendered: Option<String>,
// pub(crate) event_handler: Option<Box<DynEventHandlerFn>>,
pub(crate) disable_context_menu: bool, pub(crate) disable_context_menu: bool,
pub(crate) resource_dir: Option<PathBuf>, pub(crate) resource_dir: Option<PathBuf>,
pub(crate) custom_head: Option<String>, pub(crate) custom_head: Option<String>,
@@ -30,7 +24,6 @@ pub struct Config {
} }
type DropHandler = Box<dyn Fn(&Window, FileDropEvent) -> bool>; type DropHandler = Box<dyn Fn(&Window, FileDropEvent) -> bool>;
type EventHandler = Box<dyn Fn(&Event<UserWindowEvent>, &EventLoopWindowTarget<UserWindowEvent>)>;
pub(crate) type WryProtocol = ( pub(crate) type WryProtocol = (
String, String,
@@ -48,7 +41,6 @@ impl Config {
window, window,
protocols: Vec::new(), protocols: Vec::new(),
file_drop_handler: None, file_drop_handler: None,
event_handler: None,
pre_rendered: None, pre_rendered: None,
disable_context_menu: !cfg!(debug_assertions), disable_context_menu: !cfg!(debug_assertions),
resource_dir: None, resource_dir: None,
@@ -84,16 +76,6 @@ impl Config {
self self
} }
/// Set a custom wry event handler. This can be used to listen to window and webview events
/// This only has an effect on the main window
pub fn with_event_handler(
mut self,
handler: impl Fn(&Event<UserWindowEvent>, &EventLoopWindowTarget<UserWindowEvent>) + 'static,
) -> Self {
self.event_handler = Some(Box::new(handler));
self
}
/// Set a file drop handler /// Set a file drop handler
pub fn with_file_drop_handler( pub fn with_file_drop_handler(
mut self, mut self,
+132
View File
@@ -10,6 +10,8 @@ use crate::WebviewHandler;
use dioxus_core::ScopeState; use dioxus_core::ScopeState;
use dioxus_core::VirtualDom; use dioxus_core::VirtualDom;
use serde_json::Value; use serde_json::Value;
use slab::Slab;
use wry::application::event::Event;
use wry::application::event_loop::EventLoopProxy; use wry::application::event_loop::EventLoopProxy;
use wry::application::event_loop::EventLoopWindowTarget; use wry::application::event_loop::EventLoopWindowTarget;
#[cfg(target_os = "ios")] #[cfg(target_os = "ios")]
@@ -57,6 +59,8 @@ pub struct DesktopContext {
pub(crate) event_loop: EventLoopWindowTarget<UserWindowEvent>, pub(crate) event_loop: EventLoopWindowTarget<UserWindowEvent>,
pub(crate) event_handlers: WindowEventHandlers,
#[cfg(target_os = "ios")] #[cfg(target_os = "ios")]
pub(crate) views: Rc<RefCell<Vec<*mut objc::runtime::Object>>>, pub(crate) views: Rc<RefCell<Vec<*mut objc::runtime::Object>>>,
} }
@@ -76,6 +80,7 @@ impl DesktopContext {
proxy: ProxyType, proxy: ProxyType,
event_loop: EventLoopWindowTarget<UserWindowEvent>, event_loop: EventLoopWindowTarget<UserWindowEvent>,
webviews: WebviewQueue, webviews: WebviewQueue,
event_handlers: WindowEventHandlers,
) -> Self { ) -> Self {
Self { Self {
webview, webview,
@@ -83,6 +88,7 @@ impl DesktopContext {
event_loop, event_loop,
eval: tokio::sync::broadcast::channel(8).0, eval: tokio::sync::broadcast::channel(8).0,
pending_windows: webviews, pending_windows: webviews,
event_handlers,
#[cfg(target_os = "ios")] #[cfg(target_os = "ios")]
views: Default::default(), views: Default::default(),
} }
@@ -102,6 +108,7 @@ impl DesktopContext {
&self.proxy, &self.proxy,
dom, dom,
&self.pending_windows, &self.pending_windows,
&self.event_handlers,
); );
let id = window.webview.window().id(); let id = window.webview.window().id();
@@ -216,6 +223,22 @@ impl DesktopContext {
EvalResult::new(self.eval.clone()) EvalResult::new(self.eval.clone())
} }
/// Create a wry event handler that listens for wry events.
/// This event handler is scoped to the currently active window and will only recieve events that are either global or related to the current window.
///
/// The id this function returns can be used to remove the event handler with [`DesktopContext::remove_wry_event_handler`]
pub fn create_wry_event_handler(
&self,
handler: impl FnMut(&Event<UserWindowEvent>, &EventLoopWindowTarget<UserWindowEvent>) + 'static,
) -> WryEventHandlerId {
self.event_handlers.add(self.id(), handler)
}
/// Remove a wry event handler created with [`DesktopContext::create_wry_event_handler`]
pub fn remove_wry_event_handler(&self, id: WryEventHandlerId) {
self.event_handlers.remove(id)
}
/// Push an objc view to the window /// Push an objc view to the window
#[cfg(target_os = "ios")] #[cfg(target_os = "ios")]
pub fn push_view(&self, view: objc_id::ShareId<objc::runtime::Object>) { pub fn push_view(&self, view: objc_id::ShareId<objc::runtime::Object>) {
@@ -276,3 +299,112 @@ fn is_main_thread() -> bool {
let result: BOOL = unsafe { msg_send![cls, isMainThread] }; let result: BOOL = unsafe { msg_send![cls, isMainThread] };
result != NO result != NO
} }
/// The unique identifier of a window event handler. This can be used to later remove the handler.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct WryEventHandlerId(usize);
#[derive(Clone, Default)]
pub(crate) struct WindowEventHandlers {
handlers: Rc<RefCell<Slab<WryWindowEventHandlerInner>>>,
}
impl WindowEventHandlers {
pub(crate) fn add(
&self,
window_id: WindowId,
handler: impl FnMut(&Event<UserWindowEvent>, &EventLoopWindowTarget<UserWindowEvent>) + 'static,
) -> WryEventHandlerId {
WryEventHandlerId(
self.handlers
.borrow_mut()
.insert(WryWindowEventHandlerInner {
window_id,
handler: Box::new(handler),
}),
)
}
pub(crate) fn remove(&self, id: WryEventHandlerId) {
self.handlers.borrow_mut().try_remove(id.0);
}
pub(crate) fn apply_event(
&self,
event: &Event<UserWindowEvent>,
target: &EventLoopWindowTarget<UserWindowEvent>,
) {
for (_, handler) in self.handlers.borrow_mut().iter_mut() {
handler.apply_event(event, target);
}
}
}
struct WryWindowEventHandlerInner {
window_id: WindowId,
handler:
Box<dyn FnMut(&Event<UserWindowEvent>, &EventLoopWindowTarget<UserWindowEvent>) + 'static>,
}
impl WryWindowEventHandlerInner {
fn apply_event(
&mut self,
event: &Event<UserWindowEvent>,
target: &EventLoopWindowTarget<UserWindowEvent>,
) {
// if this event does not apply to the window this listener cares about, return
match event {
Event::WindowEvent { window_id, .. }
| Event::MenuEvent {
window_id: Some(window_id),
..
} => {
if *window_id != self.window_id {
return;
}
}
_ => (),
}
(self.handler)(event, target)
}
}
/// Get a closure that executes any JavaScript in the WebView context.
pub fn use_wry_event_handler(
cx: &ScopeState,
handler: impl FnMut(&Event<UserWindowEvent>, &EventLoopWindowTarget<UserWindowEvent>) + 'static,
) -> &WryEventHandler {
let desktop = use_window(cx);
cx.use_hook(move || {
let desktop = desktop.clone();
let id = desktop.create_wry_event_handler(handler);
WryEventHandler {
handlers: desktop.event_handlers.clone(),
id,
}
})
}
/// A wry event handler that is scoped to the current component and window. The event handler will only receive events for the window it was created for and global events.
///
/// This will automatically be removed when the component is unmounted.
pub struct WryEventHandler {
handlers: WindowEventHandlers,
/// The unique identifier of the event handler.
pub id: WryEventHandlerId,
}
impl WryEventHandler {
/// Remove the event handler.
pub fn remove(&self) {
self.handlers.remove(self.id);
}
}
impl Drop for WryEventHandler {
fn drop(&mut self) {
self.handlers.remove(self.id);
}
}
+12 -8
View File
@@ -16,8 +16,10 @@ mod webview;
mod hot_reload; mod hot_reload;
pub use cfg::Config; pub use cfg::Config;
pub use desktop_context::{use_window, DesktopContext}; pub use desktop_context::{
use desktop_context::{EventData, UserWindowEvent, WebviewQueue}; use_window, use_wry_event_handler, DesktopContext, WryEventHandler, WryEventHandlerId,
};
use desktop_context::{EventData, UserWindowEvent, WebviewQueue, WindowEventHandlers};
use dioxus_core::*; use dioxus_core::*;
use dioxus_html::HtmlEvent; use dioxus_html::HtmlEvent;
pub use eval::{use_eval, EvalResult}; pub use eval::{use_eval, EvalResult};
@@ -104,7 +106,7 @@ pub fn launch_cfg(root: Component, config_builder: Config) {
/// }) /// })
/// } /// }
/// ``` /// ```
pub fn launch_with_props<P: 'static>(root: Component<P>, props: P, mut cfg: Config) { pub fn launch_with_props<P: 'static>(root: Component<P>, props: P, cfg: Config) {
let event_loop = EventLoop::<UserWindowEvent>::with_user_event(); let event_loop = EventLoop::<UserWindowEvent>::with_user_event();
let proxy = event_loop.create_proxy(); let proxy = event_loop.create_proxy();
@@ -123,9 +125,10 @@ pub fn launch_with_props<P: 'static>(root: Component<P>, props: P, mut cfg: Conf
// Store them in a hashmap so we can remove them when they're closed // Store them in a hashmap so we can remove them when they're closed
let mut webviews = HashMap::<WindowId, WebviewHandler>::new(); let mut webviews = HashMap::<WindowId, WebviewHandler>::new();
let queue = WebviewQueue::default(); // We use this to allow dynamically adding and removing window event handlers
let event_handlers = WindowEventHandlers::default();
let event_handler = cfg.event_handler.take(); let queue = WebviewQueue::default();
// By default, we'll create a new window when the app starts // By default, we'll create a new window when the app starts
queue.borrow_mut().push(create_new_window( queue.borrow_mut().push(create_new_window(
@@ -134,14 +137,13 @@ pub fn launch_with_props<P: 'static>(root: Component<P>, props: P, mut cfg: Conf
&proxy, &proxy,
VirtualDom::new_with_props(root, props), VirtualDom::new_with_props(root, props),
&queue, &queue,
&event_handlers,
)); ));
event_loop.run(move |window_event, event_loop, control_flow| { event_loop.run(move |window_event, event_loop, control_flow| {
*control_flow = ControlFlow::Wait; *control_flow = ControlFlow::Wait;
if let Some(handler) = &event_handler { event_handlers.apply_event(&window_event, event_loop);
handler(&window_event, event_loop);
}
match window_event { match window_event {
Event::WindowEvent { Event::WindowEvent {
@@ -246,6 +248,7 @@ fn create_new_window(
proxy: &EventLoopProxy<UserWindowEvent>, proxy: &EventLoopProxy<UserWindowEvent>,
dom: VirtualDom, dom: VirtualDom,
queue: &WebviewQueue, queue: &WebviewQueue,
event_handlers: &WindowEventHandlers,
) -> WebviewHandler { ) -> WebviewHandler {
let webview = webview::build(&mut cfg, event_loop, proxy.clone()); let webview = webview::build(&mut cfg, event_loop, proxy.clone());
@@ -254,6 +257,7 @@ fn create_new_window(
proxy.clone(), proxy.clone(),
event_loop.clone(), event_loop.clone(),
queue.clone(), queue.clone(),
event_handlers.clone(),
)); ));
let id = webview.window().id(); let id = webview.window().id();