change to hook api

This commit is contained in:
Evan Almloff 2023-01-12 18:48:23 -06:00
parent 2772b86629
commit cd17f515d4
5 changed files with 187 additions and 26 deletions

42
examples/window_focus.rs Normal file
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!"
}
}
}
})
}

View file

@ -32,6 +32,7 @@ tokio = { version = "1.16.1", features = [
webbrowser = "0.8.0"
infer = "0.11.0"
dunce = "1.0.2"
slab = "0.4"
interprocess = { version = "1.1.1", optional = true }
futures-util = "0.3.25"

View file

@ -1,7 +1,5 @@
use std::path::PathBuf;
use wry::application::event::Event;
use wry::application::event_loop::EventLoopWindowTarget;
use wry::application::window::Icon;
use wry::{
application::window::{Window, WindowBuilder},
@ -10,18 +8,14 @@ use wry::{
Result as WryResult,
};
use crate::desktop_context::UserWindowEvent;
// pub(crate) type DynEventHandlerFn = dyn Fn(&mut EventLoop<()>, &mut WebView);
/// The configuration for the desktop application.
pub struct Config {
pub(crate) window: WindowBuilder,
pub(crate) file_drop_handler: Option<DropHandler>,
pub(crate) event_handler: Option<EventHandler>,
pub(crate) protocols: Vec<WryProtocol>,
pub(crate) pre_rendered: Option<String>,
// pub(crate) event_handler: Option<Box<DynEventHandlerFn>>,
pub(crate) disable_context_menu: bool,
pub(crate) resource_dir: Option<PathBuf>,
pub(crate) custom_head: Option<String>,
@ -30,7 +24,6 @@ pub struct Config {
}
type DropHandler = Box<dyn Fn(&Window, FileDropEvent) -> bool>;
type EventHandler = Box<dyn Fn(&Event<UserWindowEvent>, &EventLoopWindowTarget<UserWindowEvent>)>;
pub(crate) type WryProtocol = (
String,
@ -48,7 +41,6 @@ impl Config {
window,
protocols: Vec::new(),
file_drop_handler: None,
event_handler: None,
pre_rendered: None,
disable_context_menu: !cfg!(debug_assertions),
resource_dir: None,
@ -84,16 +76,6 @@ impl Config {
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
pub fn with_file_drop_handler(
mut self,

View file

@ -10,6 +10,8 @@ use crate::WebviewHandler;
use dioxus_core::ScopeState;
use dioxus_core::VirtualDom;
use serde_json::Value;
use slab::Slab;
use wry::application::event::Event;
use wry::application::event_loop::EventLoopProxy;
use wry::application::event_loop::EventLoopWindowTarget;
#[cfg(target_os = "ios")]
@ -57,6 +59,8 @@ pub struct DesktopContext {
pub(crate) event_loop: EventLoopWindowTarget<UserWindowEvent>,
pub(crate) event_handlers: WindowEventHandlers,
#[cfg(target_os = "ios")]
pub(crate) views: Rc<RefCell<Vec<*mut objc::runtime::Object>>>,
}
@ -76,6 +80,7 @@ impl DesktopContext {
proxy: ProxyType,
event_loop: EventLoopWindowTarget<UserWindowEvent>,
webviews: WebviewQueue,
event_handlers: WindowEventHandlers,
) -> Self {
Self {
webview,
@ -83,6 +88,7 @@ impl DesktopContext {
event_loop,
eval: tokio::sync::broadcast::channel(8).0,
pending_windows: webviews,
event_handlers,
#[cfg(target_os = "ios")]
views: Default::default(),
}
@ -102,6 +108,7 @@ impl DesktopContext {
&self.proxy,
dom,
&self.pending_windows,
&self.event_handlers,
);
let id = window.webview.window().id();
@ -216,6 +223,22 @@ impl DesktopContext {
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
#[cfg(target_os = "ios")]
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] };
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);
}
}

View file

@ -16,8 +16,10 @@ mod webview;
mod hot_reload;
pub use cfg::Config;
pub use desktop_context::{use_window, DesktopContext};
use desktop_context::{EventData, UserWindowEvent, WebviewQueue};
pub use desktop_context::{
use_window, use_wry_event_handler, DesktopContext, WryEventHandler, WryEventHandlerId,
};
use desktop_context::{EventData, UserWindowEvent, WebviewQueue, WindowEventHandlers};
use dioxus_core::*;
use dioxus_html::HtmlEvent;
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 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
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
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,
VirtualDom::new_with_props(root, props),
&queue,
&event_handlers,
));
event_loop.run(move |window_event, event_loop, control_flow| {
*control_flow = ControlFlow::Wait;
if let Some(handler) = &event_handler {
handler(&window_event, event_loop);
}
event_handlers.apply_event(&window_event, event_loop);
match window_event {
Event::WindowEvent {
@ -246,6 +248,7 @@ fn create_new_window(
proxy: &EventLoopProxy<UserWindowEvent>,
dom: VirtualDom,
queue: &WebviewQueue,
event_handlers: &WindowEventHandlers,
) -> WebviewHandler {
let webview = webview::build(&mut cfg, event_loop, proxy.clone());
@ -254,6 +257,7 @@ fn create_new_window(
proxy.clone(),
event_loop.clone(),
queue.clone(),
event_handlers.clone(),
));
let id = webview.window().id();