feat(ipc): enhance request URL checks, update wry, refactor file drop (#9100)

* feat(ipc): enhance request URL checks

* actually use req url

* fix windows, tests

* wry 0.38, file drop refactor

* wry 0.38.1

* adjust for windows [skip ci]

* wry 0.38.2 [skip ci]

* update to latest tao, wry, muda and tray-icon

* change tag

* serde

* use published crates

* downgrade cargo-platform to 0.1.7

---------

Co-authored-by: amrbashir <amr.bashir2015@gmail.com>
This commit is contained in:
Lucas Fernandes Nogueira 2024-04-01 13:03:39 -03:00 committed by GitHub
parent b78f90bc9c
commit 06833f4fa8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
45 changed files with 1061 additions and 1538 deletions

View File

@ -0,0 +1,5 @@
---
"tauri": patch:enhance
---
Enhance the IPC URL check by using the Origin header on the custom protocol IPC and the new request URI field on the postMessage IPC instead of using `Webview::url()` which only returns the URL of the main frame and is not suitable for iframes (iframe URL fetch is still not supported on Android and on Linux when using the postMessage IPC).

7
.changes/http-v1.md Normal file
View File

@ -0,0 +1,7 @@
---
'tauri': 'patch:deps'
'tauri-runtime': 'patch'
'tauri-runtime-wry': 'patch'
---
Updated `http` crate to `1.1`

View File

@ -0,0 +1,6 @@
---
"tauri-runtime": patch:breaking
"tauri-runtime-wry": patch:breaking
---
The IPC handler closure now receives a `http::Request` instead of a String representing the request body.

5
.changes/rect-strcut.md Normal file
View File

@ -0,0 +1,5 @@
---
'tauri': 'patch:feat'
---
Added `Rect` struct.

View File

@ -0,0 +1,9 @@
---
"tauri": patch:breaking
"tauri-runtime": patch:breaking
"tauri-utils": patch:breaking
"tauri-runtime-wry": patch:breaking
"@tauri-apps/api": patch:breaking
---
Rename `FileDrop` to `DragDrop` on structs, enums and enum variants. Also renamed `file_drop` to `drag_drop` on fields and function names.

View File

@ -0,0 +1,5 @@
---
'tauri-runtime': 'major:breaking'
---
Moved `window::dpi` module to the root of the crate.

9
.changes/tray-rect.md Normal file
View File

@ -0,0 +1,9 @@
---
'tauri': 'patch:breaking'
---
Refactored the tray icon event struct:
- Changed `TrayIconEvent.icon_rect` type to use the new `tauri::Rect` type.
- Removed `TrayIconEvent.x` and `TrayIconEvent.y` fields and combined them into `TrayIconEvent.position` field.
- Removed `tauri::tray::Rectangle` struct.

View File

@ -0,0 +1,5 @@
---
'tauri': 'minor:feat'
---
Add `Webview::bounds` and `Webview::set_bounds` APIs.

5
.changes/wry-0.38.md Normal file
View File

@ -0,0 +1,5 @@
---
"tauri-runtime-wry": patch:deps
---
Upgraded to `wry@0.38.0`

926
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -221,8 +221,8 @@
"null"
]
},
"fileDropEnabled": {
"description": "Whether the file drop is enabled or not on the webview. By default it is enabled.\n\nDisabling it is required to use drag and drop on the frontend on Windows.",
"dragDropEnabled": {
"description": "Whether the drag and drop is enabled or not on the webview. By default it is enabled.\n\nDisabling it is required to use HTML5 drag and drop on the frontend on Windows.",
"default": true,
"type": "boolean"
},

View File

@ -13,22 +13,22 @@ edition = { workspace = true }
rust-version = { workspace = true }
[dependencies]
wry = { version = "0.37", default-features = false, features = [ "file-drop", "protocol", "os-webview" ] }
tao = { version = "0.26", default-features = false, features = [ "rwh_06" ] }
wry = { version = "0.39", default-features = false, features = [ "drag-drop", "protocol", "os-webview" ] }
tao = { version = "0.27", default-features = false, features = [ "rwh_06" ] }
tauri-runtime = { version = "2.0.0-beta.10", path = "../tauri-runtime" }
tauri-utils = { version = "2.0.0-beta.10", path = "../tauri-utils" }
raw-window-handle = "0.6"
http = "0.2"
http = "1.1"
url = "2"
tracing = { version = "0.1", optional = true }
log = "0.4"
[target."cfg(windows)".dependencies]
webview2-com = "0.28"
softbuffer = "0.4"
webview2-com = "0.29"
softbuffer = { version = "0.4", default-features = false }
[target."cfg(windows)".dependencies.windows]
version = "0.52"
version = "0.54"
features = [ "Win32_Foundation" ]
[target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies]

View File

@ -11,14 +11,15 @@
html_favicon_url = "https://github.com/tauri-apps/tauri/raw/dev/app-icon.png"
)]
use http::Request;
use raw_window_handle::{DisplayHandle, HasDisplayHandle, HasWindowHandle};
use tauri_runtime::{
dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size},
monitor::Monitor,
webview::{DetachedWebview, DownloadEvent, PendingWebview, WebviewIpcHandler},
window::{
dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size},
CursorIcon, DetachedWindow, FileDropEvent, PendingWindow, RawWindow, WebviewEvent,
CursorIcon, DetachedWindow, DragDropEvent, PendingWindow, RawWindow, WebviewEvent,
WindowBuilder, WindowBuilderBase, WindowEvent, WindowId,
},
DeviceEventFilter, Error, EventLoopProxy, ExitRequestedEventAction, Icon, ProgressBarState,
@ -62,7 +63,7 @@ use tauri_utils::TitleBarStyle;
use tauri_utils::{config::WindowConfig, Theme};
use url::Url;
use wry::{
FileDropEvent as WryFileDropEvent, ProxyConfig, ProxyEndpoint, WebContext, WebView,
DragDropEvent as WryDragDropEvent, ProxyConfig, ProxyEndpoint, WebContext, WebView,
WebViewBuilder,
};
@ -112,7 +113,7 @@ use std::{
};
pub type WebviewId = u32;
type IpcHandler = dyn Fn(String) + 'static;
type IpcHandler = dyn Fn(Request<String>) + 'static;
#[cfg(any(
windows,
@ -415,6 +416,16 @@ impl From<DeviceEventFilter> for DeviceEventFilterWrapper {
}
}
pub struct RectWrapper(pub wry::Rect);
impl From<tauri_runtime::Rect> for RectWrapper {
fn from(value: tauri_runtime::Rect) -> Self {
RectWrapper(wry::Rect {
position: value.position,
size: value.size,
})
}
}
/// Wrapper around a [`tao::window::Icon`] that can be created from an [`Icon`].
pub struct TaoIcon(pub TaoWindowIcon);
@ -1144,14 +1155,14 @@ pub enum WindowMessage {
#[derive(Debug, Clone)]
pub enum SynthesizedWindowEvent {
Focused(bool),
FileDrop(FileDropEvent),
DragDrop(DragDropEvent),
}
impl From<SynthesizedWindowEvent> for WindowEventWrapper {
fn from(event: SynthesizedWindowEvent) -> Self {
let event = match event {
SynthesizedWindowEvent::Focused(focused) => WindowEvent::Focused(focused),
SynthesizedWindowEvent::FileDrop(event) => WindowEvent::FileDrop(event),
SynthesizedWindowEvent::DragDrop(event) => WindowEvent::DragDrop(event),
};
Self(Some(event))
}
@ -1170,13 +1181,15 @@ pub enum WebviewMessage {
Close,
SetPosition(Position),
SetSize(Size),
SetBounds(tauri_runtime::Rect),
SetFocus,
Reparent(WindowId),
Reparent(WindowId, Sender<Result<()>>),
SetAutoResize(bool),
// Getters
Url(Sender<Url>),
Position(Sender<PhysicalPosition<i32>>),
Size(Sender<PhysicalSize<u32>>),
Url(Sender<Result<Url>>),
Bounds(Sender<Result<tauri_runtime::Rect>>),
Position(Sender<Result<PhysicalPosition<i32>>>),
Size(Sender<Result<PhysicalSize<u32>>>),
WithWebview(Box<dyn FnOnce(Webview) + Send>),
// Devtools
#[cfg(any(debug_assertions, feature = "devtools"))]
@ -1289,15 +1302,19 @@ impl<T: UserEvent> WebviewDispatch<T> for WryWebviewDispatcher<T> {
// Getters
fn url(&self) -> Result<Url> {
webview_getter!(self, WebviewMessage::Url)
webview_getter!(self, WebviewMessage::Url)?
}
fn bounds(&self) -> Result<tauri_runtime::Rect> {
webview_getter!(self, WebviewMessage::Bounds)?
}
fn position(&self) -> Result<PhysicalPosition<i32>> {
webview_getter!(self, WebviewMessage::Position)
webview_getter!(self, WebviewMessage::Position)?
}
fn size(&self) -> Result<PhysicalSize<u32>> {
webview_getter!(self, WebviewMessage::Size)
webview_getter!(self, WebviewMessage::Size)?
}
// Setters
@ -1335,6 +1352,17 @@ impl<T: UserEvent> WebviewDispatch<T> for WryWebviewDispatcher<T> {
)
}
fn set_bounds(&self, bounds: tauri_runtime::Rect) -> Result<()> {
send_user_message(
&self.context,
Message::Webview(
*self.window_id.lock().unwrap(),
self.webview_id,
WebviewMessage::SetBounds(bounds),
),
)
}
fn set_size(&self, size: Size) -> Result<()> {
send_user_message(
&self.context,
@ -1370,15 +1398,18 @@ impl<T: UserEvent> WebviewDispatch<T> for WryWebviewDispatcher<T> {
fn reparent(&self, window_id: WindowId) -> Result<()> {
let mut current_window_id = self.window_id.lock().unwrap();
let (tx, rx) = channel();
send_user_message(
&self.context,
Message::Webview(
*current_window_id,
self.webview_id,
WebviewMessage::Reparent(window_id),
WebviewMessage::Reparent(window_id, tx),
),
)?;
rx.recv().unwrap()?;
*current_window_id = window_id;
Ok(())
}
@ -2746,7 +2777,7 @@ fn handle_user_message<T: UserEvent>(
target_os = "netbsd",
target_os = "openbsd"
))]
if let WebviewMessage::Reparent(new_parent_window_id) = webview_message {
if let WebviewMessage::Reparent(new_parent_window_id, tx) = webview_message {
let webview_handle = windows.0.borrow_mut().get_mut(&window_id).and_then(|w| {
w.webviews
.iter()
@ -2762,16 +2793,13 @@ fn handle_user_message<T: UserEvent>(
.map(|w| (w.inner.clone(), &mut w.webviews))
{
#[cfg(target_os = "macos")]
{
let reparent_result = {
use wry::WebViewExtMacOS;
webview.inner.reparent(new_parent_window.ns_window() as _);
new_parent_window_webviews.push(webview);
}
webview.inner.reparent(new_parent_window.ns_window() as _)
};
#[cfg(windows)]
{
webview.inner.reparent(new_parent_window.hwnd());
new_parent_window_webviews.push(webview);
}
let reparent_result = { webview.inner.reparent(new_parent_window.hwnd()) };
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
@ -2779,13 +2807,27 @@ fn handle_user_message<T: UserEvent>(
target_os = "netbsd",
target_os = "openbsd"
))]
{
let reparent_result = {
if let Some(container) = new_parent_window.default_vbox() {
webview.inner.reparent(container);
webview.inner.reparent(container)
} else {
Err(wry::Error::MessageSender)
}
};
match reparent_result {
Ok(_) => {
new_parent_window_webviews.push(webview);
tx.send(Ok(())).unwrap();
}
Err(e) => {
log::error!("failed to reparent webview: {e}");
tx.send(Err(Error::FailedToSendMessage)).unwrap();
}
}
}
} else {
tx.send(Err(Error::FailedToSendMessage)).unwrap();
}
return;
@ -2801,7 +2843,7 @@ fn handle_user_message<T: UserEvent>(
match webview_message {
WebviewMessage::WebviewEvent(_) => { /* already handled */ }
WebviewMessage::SynthesizedWindowEvent(_) => { /* already handled */ }
WebviewMessage::Reparent(_window_id) => { /* already handled */ }
WebviewMessage::Reparent(_window_id, _tx) => { /* already handled */ }
WebviewMessage::AddEventListener(id, listener) => {
webview
.webview_event_listeners
@ -2824,7 +2866,11 @@ fn handle_user_message<T: UserEvent>(
log::error!("{}", e);
}
}
WebviewMessage::Navigate(url) => webview.load_url(url.as_str()),
WebviewMessage::Navigate(url) => {
if let Err(e) = webview.load_url(url.as_str()) {
log::error!("failed to navigate to url {}: {}", url, e);
}
}
WebviewMessage::Print => {
let _ = webview.print();
}
@ -2836,67 +2882,133 @@ fn handle_user_message<T: UserEvent>(
window
});
}
WebviewMessage::SetSize(size) => {
let mut bounds = webview.bounds();
let size = size.to_logical(window.scale_factor());
bounds.width = size.width;
bounds.height = size.height;
WebviewMessage::SetBounds(bounds) => {
let bounds: RectWrapper = bounds.into();
let bounds = bounds.0;
if let Some(b) = &mut *webview.bounds.lock().unwrap() {
let window_size = window.inner_size().to_logical::<f32>(window.scale_factor());
b.width_rate = size.width as f32 / window_size.width;
b.height_rate = size.height as f32 / window_size.height;
let scale_factor = window.scale_factor();
let size = bounds.size.to_logical::<f32>(scale_factor);
let position = bounds.position.to_logical::<f32>(scale_factor);
let window_size = window.inner_size().to_logical::<f32>(scale_factor);
b.width_rate = size.width / window_size.width;
b.height_rate = size.height / window_size.height;
b.x_rate = position.x / window_size.width;
b.y_rate = position.y / window_size.height;
}
webview.set_bounds(bounds);
}
WebviewMessage::SetPosition(position) => {
let mut bounds = webview.bounds();
let position = position.to_logical(window.scale_factor());
bounds.x = position.x;
bounds.y = position.y;
if let Some(b) = &mut *webview.bounds.lock().unwrap() {
let window_size = window.inner_size().to_logical::<f32>(window.scale_factor());
b.x_rate = position.x as f32 / window_size.width;
b.y_rate = position.y as f32 / window_size.height;
if let Err(e) = webview.set_bounds(bounds) {
log::error!("failed to set webview size: {e}");
}
webview.set_bounds(bounds);
}
WebviewMessage::SetSize(size) => match webview.bounds() {
Ok(mut bounds) => {
bounds.size = size;
let scale_factor = window.scale_factor();
let size = size.to_logical::<f32>(scale_factor);
if let Some(b) = &mut *webview.bounds.lock().unwrap() {
let window_size = window.inner_size().to_logical::<f32>(scale_factor);
b.width_rate = size.width / window_size.width;
b.height_rate = size.height / window_size.height;
}
if let Err(e) = webview.set_bounds(bounds) {
log::error!("failed to set webview size: {e}");
}
}
Err(e) => {
log::error!("failed to get webview bounds: {e}");
}
},
WebviewMessage::SetPosition(position) => match webview.bounds() {
Ok(mut bounds) => {
bounds.position = position;
let scale_factor = window.scale_factor();
let position = position.to_logical::<f32>(scale_factor);
if let Some(b) = &mut *webview.bounds.lock().unwrap() {
let window_size = window.inner_size().to_logical::<f32>(scale_factor);
b.x_rate = position.x / window_size.width;
b.y_rate = position.y / window_size.height;
}
if let Err(e) = webview.set_bounds(bounds) {
log::error!("failed to set webview position: {e}");
}
}
Err(e) => {
log::error!("failed to get webview bounds: {e}");
}
},
// Getters
WebviewMessage::Url(tx) => {
tx.send(webview.url().parse().unwrap()).unwrap();
tx.send(
webview
.url()
.map(|u| u.parse().expect("invalid webview URL"))
.map_err(|_| Error::FailedToSendMessage),
)
.unwrap();
}
WebviewMessage::Bounds(tx) => {
tx.send(
webview
.bounds()
.map(|bounds| tauri_runtime::Rect {
size: bounds.size,
position: bounds.position,
})
.map_err(|_| Error::FailedToSendMessage),
)
.unwrap();
}
WebviewMessage::Position(tx) => {
let bounds = webview.bounds();
let position =
LogicalPosition::new(bounds.x, bounds.y).to_physical(window.scale_factor());
tx.send(position).unwrap();
tx.send(
webview
.bounds()
.map(|bounds| bounds.position.to_physical(window.scale_factor()))
.map_err(|_| Error::FailedToSendMessage),
)
.unwrap();
}
WebviewMessage::Size(tx) => {
let bounds = webview.bounds();
let size =
LogicalSize::new(bounds.width, bounds.height).to_physical(window.scale_factor());
tx.send(size).unwrap();
tx.send(
webview
.bounds()
.map(|bounds| bounds.size.to_physical(window.scale_factor()))
.map_err(|_| Error::FailedToSendMessage),
)
.unwrap();
}
WebviewMessage::SetFocus => {
webview.focus();
}
WebviewMessage::SetAutoResize(auto_resize) => {
let bounds = webview.bounds();
let window_size = window.inner_size().to_logical::<f32>(window.scale_factor());
*webview.bounds.lock().unwrap() = if auto_resize {
Some(WebviewBounds {
x_rate: (bounds.x as f32) / window_size.width,
y_rate: (bounds.y as f32) / window_size.height,
width_rate: (bounds.width as f32) / window_size.width,
height_rate: (bounds.height as f32) / window_size.height,
})
} else {
None
};
if let Err(e) = webview.focus() {
log::error!("failed to focus webview: {e}");
}
}
WebviewMessage::SetAutoResize(auto_resize) => match webview.bounds() {
Ok(bounds) => {
let scale_factor = window.scale_factor();
let window_size = window.inner_size().to_logical::<f32>(scale_factor);
*webview.bounds.lock().unwrap() = if auto_resize {
let size = bounds.size.to_logical::<f32>(scale_factor);
let position = bounds.position.to_logical::<f32>(scale_factor);
Some(WebviewBounds {
x_rate: position.x / window_size.width,
y_rate: position.y / window_size.height,
width_rate: size.width / window_size.width,
height_rate: size.height / window_size.height,
})
} else {
None
};
}
Err(e) => {
log::error!("failed to get webview bounds: {e}");
}
},
WebviewMessage::WithWebview(f) => {
#[cfg(any(
target_os = "linux",
@ -3182,7 +3294,9 @@ fn handle_event_loop<T: UserEvent>(
TaoTheme::Light => wry::Theme::Light,
_ => wry::Theme::Light,
};
webview.set_theme(theme);
if let Err(e) = webview.set_theme(theme) {
log::error!("failed to set theme: {e}");
}
}
}
}
@ -3216,12 +3330,14 @@ fn handle_event_loop<T: UserEvent>(
let size = size.to_logical::<f32>(window.scale_factor());
for webview in &webviews {
if let Some(b) = &*webview.bounds.lock().unwrap() {
webview.set_bounds(wry::Rect {
x: (size.width * b.x_rate) as i32,
y: (size.height * b.y_rate) as i32,
width: (size.width * b.width_rate) as u32,
height: (size.height * b.height_rate) as u32,
});
if let Err(e) = webview.set_bounds(wry::Rect {
position: LogicalPosition::new(size.width * b.x_rate, size.height * b.y_rate)
.into(),
size: LogicalSize::new(size.width * b.width_rate, size.height * b.height_rate)
.into(),
}) {
log::error!("failed to autoresize webview: {e}");
}
}
}
#[cfg(windows)]
@ -3590,33 +3706,36 @@ fn create_webview<T: UserEvent>(
webview_builder = webview_builder.with_initialization_script(undecorated_resizing::SCRIPT);
}
if webview_attributes.file_drop_handler_enabled {
if webview_attributes.drag_drop_handler_enabled {
let proxy = context.proxy.clone();
let window_id_ = window_id.clone();
webview_builder = webview_builder.with_file_drop_handler(move |event| {
webview_builder = webview_builder.with_drag_drop_handler(move |event| {
let event = match event {
WryFileDropEvent::Hovered {
WryDragDropEvent::Enter {
paths,
position: (x, y),
} => FileDropEvent::Hovered {
} => DragDropEvent::Dragged {
paths,
position: PhysicalPosition::new(x as _, y as _),
},
WryFileDropEvent::Dropped {
WryDragDropEvent::Over { position: (x, y) } => DragDropEvent::DragOver {
position: PhysicalPosition::new(x as _, y as _),
},
WryDragDropEvent::Drop {
paths,
position: (x, y),
} => FileDropEvent::Dropped {
} => DragDropEvent::Dropped {
paths,
position: PhysicalPosition::new(x as _, y as _),
},
WryFileDropEvent::Cancelled => FileDropEvent::Cancelled,
WryDragDropEvent::Leave => DragDropEvent::Cancelled,
_ => unimplemented!(),
};
let message = if kind == WebviewKind::WindowContent {
WebviewMessage::SynthesizedWindowEvent(SynthesizedWindowEvent::FileDrop(event))
WebviewMessage::SynthesizedWindowEvent(SynthesizedWindowEvent::DragDrop(event))
} else {
WebviewMessage::WebviewEvent(WebviewEvent::FileDrop(event))
WebviewMessage::WebviewEvent(WebviewEvent::DragDrop(event))
};
let _ = proxy.send_event(Message::Webview(*window_id_.lock().unwrap(), id, message));
@ -3633,24 +3752,24 @@ fn create_webview<T: UserEvent>(
});
}
let webview_bounds = if let Some((position, size)) = webview_attributes.bounds {
let size = size.to_logical(window.scale_factor());
let position = position.to_logical(window.scale_factor());
webview_builder = webview_builder.with_bounds(wry::Rect {
x: position.x,
y: position.y,
width: size.width,
height: size.height,
});
let webview_bounds = if let Some(bounds) = webview_attributes.bounds {
let bounds: RectWrapper = bounds.into();
let bounds = bounds.0;
let window_size = window.inner_size().to_logical::<f32>(window.scale_factor());
let scale_factor = window.scale_factor();
let position = bounds.position.to_logical::<f32>(scale_factor);
let size = bounds.size.to_logical::<f32>(scale_factor);
webview_builder = webview_builder.with_bounds(bounds);
let window_size = window.inner_size().to_logical::<f32>(scale_factor);
if webview_attributes.auto_resize {
Some(WebviewBounds {
x_rate: (position.x as f32) / window_size.width,
y_rate: (position.y as f32) / window_size.height,
width_rate: (size.width as f32) / window_size.width,
height_rate: (size.height as f32) / window_size.height,
x_rate: position.x / window_size.width,
y_rate: position.y / window_size.height,
width_rate: size.width / window_size.width,
height_rate: size.height / window_size.height,
})
} else {
None
@ -3658,13 +3777,9 @@ fn create_webview<T: UserEvent>(
} else {
#[cfg(feature = "unstable")]
{
let window_size = window.inner_size().to_logical::<u32>(window.scale_factor());
webview_builder = webview_builder.with_bounds(wry::Rect {
x: 0,
y: 0,
width: window_size.width,
height: window_size.height,
position: LogicalPosition::new(0, 0).into(),
size: window.inner_size().into(),
});
Some(WebviewBounds {
x_rate: 0.,

View File

@ -154,9 +154,9 @@ mod windows {
pub fn handle_request<T: crate::UserEvent>(
context: crate::Context<T>,
window_id: crate::WindowId,
request: &str,
request: &http::Request<String>,
) -> bool {
if let Some(args) = request.strip_prefix(MESSAGE_MOUSEMOVE) {
if let Some(args) = request.body().strip_prefix(MESSAGE_MOUSEMOVE) {
if let Some(window) = context.main_thread.windows.0.borrow().get(&window_id) {
if let Some(w) = window.inner.as_ref() {
if !w.is_decorated()
@ -177,7 +177,7 @@ mod windows {
return true;
}
if let Some(args) = request.strip_prefix(MESSAGE_MOUSEDOWN) {
if let Some(args) = request.body().strip_prefix(MESSAGE_MOUSEDOWN) {
if let Some(window) = context.main_thread.windows.0.borrow().get(&window_id) {
if let Some(w) = window.inner.as_ref() {
if !w.is_decorated()

View File

@ -30,12 +30,13 @@ serde = { version = "1.0", features = [ "derive" ] }
serde_json = "1.0"
thiserror = "1.0"
tauri-utils = { version = "2.0.0-beta.10", path = "../tauri-utils" }
http = "0.2.4"
http = "1.1"
raw-window-handle = "0.6"
url = { version = "2" }
dpi = { version = "0.1", features = ["serde"] }
[target."cfg(windows)".dependencies.windows]
version = "0.52"
version = "0.54"
features = [ "Win32_Foundation" ]
[target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies]

View File

@ -13,7 +13,7 @@
#![cfg_attr(docsrs, feature(doc_cfg))]
use raw_window_handle::DisplayHandle;
use serde::Deserialize;
use serde::{Deserialize, Serialize};
use std::{borrow::Cow, fmt::Debug, sync::mpsc::Sender};
use tauri_utils::Theme;
use url::Url;
@ -24,11 +24,9 @@ pub mod monitor;
pub mod webview;
pub mod window;
use dpi::{PhysicalPosition, PhysicalSize, Position, Size};
use monitor::Monitor;
use window::{
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
CursorIcon, DetachedWindow, PendingWindow, RawWindow, WebviewEvent, WindowEvent,
};
use window::{CursorIcon, DetachedWindow, PendingWindow, RawWindow, WebviewEvent, WindowEvent};
use window::{WindowBuilder, WindowId};
use http::{
@ -37,9 +35,30 @@ use http::{
status::InvalidStatusCode,
};
/// UI scaling utilities.
pub use dpi;
pub type WindowEventId = u32;
pub type WebviewEventId = u32;
/// A rectangular region.
#[derive(Clone, Copy, Debug, Serialize)]
pub struct Rect {
/// Rect position.
pub position: dpi::Position,
/// Rect size.
pub size: dpi::Size,
}
impl Default for Rect {
fn default() -> Self {
Self {
position: Position::Logical((0, 0).into()),
size: Size::Logical((0, 0).into()),
}
}
}
/// Progress bar status.
#[derive(Debug, Clone, Copy, Deserialize)]
#[serde(rename_all = "camelCase")]
@ -421,6 +440,9 @@ pub trait WebviewDispatch<T: UserEvent>: Debug + Clone + Send + Sync + Sized + '
/// Returns the webview's current URL.
fn url(&self) -> Result<Url>;
/// Returns the webview's bounds.
fn bounds(&self) -> Result<Rect>;
/// Returns the position of the top-left hand corner of the webviews's client area relative to the top-left hand corner of the window.
fn position(&self) -> Result<PhysicalPosition<i32>>;
@ -438,6 +460,9 @@ pub trait WebviewDispatch<T: UserEvent>: Debug + Clone + Send + Sync + Sized + '
/// Closes the webview.
fn close(&self) -> Result<()>;
/// Sets the webview's bounds.
fn set_bounds(&self, bounds: Rect) -> Result<()>;
/// Resizes the webview.
fn set_size(&self, size: Size) -> Result<()>;

View File

@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use super::window::dpi::{PhysicalPosition, PhysicalSize};
use crate::dpi::{PhysicalPosition, PhysicalSize};
/// Monitor descriptor.
#[derive(Debug, Clone)]

View File

@ -4,14 +4,9 @@
//! A layer between raw [`Runtime`] webviews and Tauri.
//!
use crate::{
window::{
dpi::{Position, Size},
is_label_valid,
},
Runtime, UserEvent,
};
use crate::{window::is_label_valid, Rect, Runtime, UserEvent};
use http::Request;
use tauri_utils::config::{WebviewUrl, WindowConfig, WindowEffectsConfig};
use url::Url;
@ -202,14 +197,14 @@ pub struct WebviewAttributes {
pub user_agent: Option<String>,
pub initialization_scripts: Vec<String>,
pub data_directory: Option<PathBuf>,
pub file_drop_handler_enabled: bool,
pub drag_drop_handler_enabled: bool,
pub clipboard: bool,
pub accept_first_mouse: bool,
pub additional_browser_args: Option<String>,
pub window_effects: Option<WindowEffectsConfig>,
pub incognito: bool,
pub transparent: bool,
pub bounds: Option<(Position, Size)>,
pub bounds: Option<Rect>,
pub auto_resize: bool,
pub proxy_url: Option<Url>,
}
@ -223,8 +218,8 @@ impl From<&WindowConfig> for WebviewAttributes {
builder = builder.transparent(config.transparent);
}
builder = builder.accept_first_mouse(config.accept_first_mouse);
if !config.file_drop_enabled {
builder = builder.disable_file_drop_handler();
if !config.drag_drop_enabled {
builder = builder.disable_drag_drop_handler();
}
if let Some(user_agent) = &config.user_agent {
builder = builder.user_agent(user_agent);
@ -250,7 +245,7 @@ impl WebviewAttributes {
user_agent: None,
initialization_scripts: Vec::new(),
data_directory: None,
file_drop_handler_enabled: true,
drag_drop_handler_enabled: true,
clipboard: false,
accept_first_mouse: false,
additional_browser_args: None,
@ -284,10 +279,10 @@ impl WebviewAttributes {
self
}
/// Disables the file drop handler. This is required to use drag and drop APIs on the front end on Windows.
/// Disables the drag and drop handler. This is required to use HTML5 drag and drop APIs on the frontend on Windows.
#[must_use]
pub fn disable_file_drop_handler(mut self) -> Self {
self.file_drop_handler_enabled = false;
pub fn disable_drag_drop_handler(mut self) -> Self {
self.drag_drop_handler_enabled = false;
self
}
@ -353,4 +348,4 @@ impl WebviewAttributes {
}
/// IPC handler.
pub type WebviewIpcHandler<T, R> = Box<dyn Fn(DetachedWebview<T, R>, String) + Send>;
pub type WebviewIpcHandler<T, R> = Box<dyn Fn(DetachedWebview<T, R>, Request<String>) + Send>;

View File

@ -21,11 +21,6 @@ use std::{
sync::mpsc::Sender,
};
use self::dpi::PhysicalPosition;
/// UI scaling utilities.
pub mod dpi;
/// An event from a window.
#[derive(Debug, Clone)]
pub enum WindowEvent {
@ -57,8 +52,8 @@ pub enum WindowEvent {
/// The window inner size.
new_inner_size: dpi::PhysicalSize<u32>,
},
/// An event associated with the file drop action.
FileDrop(FileDropEvent),
/// An event associated with the drag and drop action.
DragDrop(DragDropEvent),
/// The system window theme has changed.
///
/// Applications might wish to react to this to change the theme of the content of the window when the system changes the window theme.
@ -68,27 +63,34 @@ pub enum WindowEvent {
/// An event from a window.
#[derive(Debug, Clone)]
pub enum WebviewEvent {
/// An event associated with the file drop action.
FileDrop(FileDropEvent),
/// An event associated with the drag and drop action.
DragDrop(DragDropEvent),
}
/// The file drop event payload.
/// The drag drop event payload.
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum FileDropEvent {
/// The file(s) have been dragged onto the window, but have not been dropped yet.
Hovered {
pub enum DragDropEvent {
/// A drag operation started.
Dragged {
/// Paths of the files that are being dragged.
paths: Vec<PathBuf>,
/// The position of the mouse cursor.
position: PhysicalPosition<f64>,
position: dpi::PhysicalPosition<f64>,
},
/// The file(s) have been dropped onto the window.
/// The files have been dragged onto the window, but have not been dropped yet.
DragOver {
/// The position of the mouse cursor.
position: dpi::PhysicalPosition<f64>,
},
/// The user dropped the operation.
Dropped {
/// Path of the files that were dropped.
paths: Vec<PathBuf>,
/// The position of the mouse cursor.
position: PhysicalPosition<f64>,
position: dpi::PhysicalPosition<f64>,
},
/// The file drop was aborted.
/// The drag operation was cancelled.
Cancelled,
}

View File

@ -1,401 +0,0 @@
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use serde::{Deserialize, Serialize};
pub trait Pixel: Copy + Into<f64> {
fn from_f64(f: f64) -> Self;
fn cast<P: Pixel>(self) -> P {
P::from_f64(self.into())
}
}
impl Pixel for u8 {
fn from_f64(f: f64) -> Self {
f.round() as u8
}
}
impl Pixel for u16 {
fn from_f64(f: f64) -> Self {
f.round() as u16
}
}
impl Pixel for u32 {
fn from_f64(f: f64) -> Self {
f.round() as u32
}
}
impl Pixel for i8 {
fn from_f64(f: f64) -> Self {
f.round() as i8
}
}
impl Pixel for i16 {
fn from_f64(f: f64) -> Self {
f.round() as i16
}
}
impl Pixel for i32 {
fn from_f64(f: f64) -> Self {
f.round() as i32
}
}
impl Pixel for f32 {
fn from_f64(f: f64) -> Self {
f as f32
}
}
impl Pixel for f64 {
fn from_f64(f: f64) -> Self {
f
}
}
/// Checks that the scale factor is a normal positive `f64`.
///
/// All functions that take a scale factor assert that this will return `true`. If you're sourcing scale factors from
/// anywhere other than tao, it's recommended to validate them using this function before passing them to tao;
/// otherwise, you risk panics.
#[inline]
pub fn validate_scale_factor(scale_factor: f64) -> bool {
scale_factor.is_sign_positive() && scale_factor.is_normal()
}
/// A position represented in logical pixels.
///
/// The position is stored as floats, so please be careful. Casting floats to integers truncates the
/// fractional part, which can cause noticeable issues. To help with that, an `Into<(i32, i32)>`
/// implementation is provided which does the rounding for you.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash, Serialize, Deserialize)]
pub struct LogicalPosition<P> {
pub x: P,
pub y: P,
}
impl<P> LogicalPosition<P> {
#[inline]
pub const fn new(x: P, y: P) -> Self {
LogicalPosition { x, y }
}
}
impl<P: Pixel> LogicalPosition<P> {
#[inline]
pub fn from_physical<T: Into<PhysicalPosition<X>>, X: Pixel>(
physical: T,
scale_factor: f64,
) -> Self {
physical.into().to_logical(scale_factor)
}
#[inline]
pub fn to_physical<X: Pixel>(&self, scale_factor: f64) -> PhysicalPosition<X> {
assert!(validate_scale_factor(scale_factor));
let x = self.x.into() * scale_factor;
let y = self.y.into() * scale_factor;
PhysicalPosition::new(x, y).cast()
}
#[inline]
pub fn cast<X: Pixel>(&self) -> LogicalPosition<X> {
LogicalPosition {
x: self.x.cast(),
y: self.y.cast(),
}
}
}
impl<P: Pixel, X: Pixel> From<(X, X)> for LogicalPosition<P> {
fn from((x, y): (X, X)) -> LogicalPosition<P> {
LogicalPosition::new(x.cast(), y.cast())
}
}
impl<P: Pixel, X: Pixel> From<LogicalPosition<P>> for (X, X) {
fn from(pos: LogicalPosition<P>) -> Self {
(pos.x.cast(), pos.y.cast())
}
}
impl<P: Pixel, X: Pixel> From<[X; 2]> for LogicalPosition<P> {
fn from([x, y]: [X; 2]) -> LogicalPosition<P> {
LogicalPosition::new(x.cast(), y.cast())
}
}
impl<P: Pixel, X: Pixel> From<LogicalPosition<P>> for [X; 2] {
fn from(pos: LogicalPosition<P>) -> Self {
[pos.x.cast(), pos.y.cast()]
}
}
/// A position represented in physical pixels.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash, Serialize, Deserialize)]
pub struct PhysicalPosition<P> {
pub x: P,
pub y: P,
}
impl<P> PhysicalPosition<P> {
#[inline]
pub const fn new(x: P, y: P) -> Self {
PhysicalPosition { x, y }
}
}
impl<P: Pixel> PhysicalPosition<P> {
#[inline]
pub fn from_logical<T: Into<LogicalPosition<X>>, X: Pixel>(
logical: T,
scale_factor: f64,
) -> Self {
logical.into().to_physical(scale_factor)
}
#[inline]
pub fn to_logical<X: Pixel>(&self, scale_factor: f64) -> LogicalPosition<X> {
assert!(validate_scale_factor(scale_factor));
let x = self.x.into() / scale_factor;
let y = self.y.into() / scale_factor;
LogicalPosition::new(x, y).cast()
}
#[inline]
pub fn cast<X: Pixel>(&self) -> PhysicalPosition<X> {
PhysicalPosition {
x: self.x.cast(),
y: self.y.cast(),
}
}
}
impl<P: Pixel, X: Pixel> From<(X, X)> for PhysicalPosition<P> {
fn from((x, y): (X, X)) -> PhysicalPosition<P> {
PhysicalPosition::new(x.cast(), y.cast())
}
}
impl<P: Pixel, X: Pixel> From<PhysicalPosition<P>> for (X, X) {
fn from(pos: PhysicalPosition<P>) -> Self {
(pos.x.cast(), pos.y.cast())
}
}
impl<P: Pixel, X: Pixel> From<[X; 2]> for PhysicalPosition<P> {
fn from([x, y]: [X; 2]) -> PhysicalPosition<P> {
PhysicalPosition::new(x.cast(), y.cast())
}
}
impl<P: Pixel, X: Pixel> From<PhysicalPosition<P>> for [X; 2] {
fn from(pos: PhysicalPosition<P>) -> Self {
[pos.x.cast(), pos.y.cast()]
}
}
/// A size represented in logical pixels.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash, Serialize, Deserialize)]
pub struct LogicalSize<P> {
pub width: P,
pub height: P,
}
impl<P> LogicalSize<P> {
#[inline]
pub const fn new(width: P, height: P) -> Self {
LogicalSize { width, height }
}
}
impl<P: Pixel> LogicalSize<P> {
#[inline]
pub fn from_physical<T: Into<PhysicalSize<X>>, X: Pixel>(physical: T, scale_factor: f64) -> Self {
physical.into().to_logical(scale_factor)
}
#[inline]
pub fn to_physical<X: Pixel>(&self, scale_factor: f64) -> PhysicalSize<X> {
assert!(validate_scale_factor(scale_factor));
let width = self.width.into() * scale_factor;
let height = self.height.into() * scale_factor;
PhysicalSize::new(width, height).cast()
}
#[inline]
pub fn cast<X: Pixel>(&self) -> LogicalSize<X> {
LogicalSize {
width: self.width.cast(),
height: self.height.cast(),
}
}
}
impl<P: Pixel, X: Pixel> From<(X, X)> for LogicalSize<P> {
fn from((x, y): (X, X)) -> LogicalSize<P> {
LogicalSize::new(x.cast(), y.cast())
}
}
impl<P: Pixel, X: Pixel> From<LogicalSize<P>> for (X, X) {
fn from(size: LogicalSize<P>) -> Self {
(size.width.cast(), size.height.cast())
}
}
impl<P: Pixel, X: Pixel> From<[X; 2]> for LogicalSize<P> {
fn from([x, y]: [X; 2]) -> LogicalSize<P> {
LogicalSize::new(x.cast(), y.cast())
}
}
impl<P: Pixel, X: Pixel> From<LogicalSize<P>> for [X; 2] {
fn from(size: LogicalSize<P>) -> Self {
[size.width.cast(), size.height.cast()]
}
}
/// A size represented in physical pixels.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default, Hash, Serialize, Deserialize)]
pub struct PhysicalSize<P> {
pub width: P,
pub height: P,
}
impl<P> PhysicalSize<P> {
#[inline]
pub const fn new(width: P, height: P) -> Self {
PhysicalSize { width, height }
}
}
impl<P: Pixel> PhysicalSize<P> {
#[inline]
pub fn from_logical<T: Into<LogicalSize<X>>, X: Pixel>(logical: T, scale_factor: f64) -> Self {
logical.into().to_physical(scale_factor)
}
#[inline]
pub fn to_logical<X: Pixel>(&self, scale_factor: f64) -> LogicalSize<X> {
assert!(validate_scale_factor(scale_factor));
let width = self.width.into() / scale_factor;
let height = self.height.into() / scale_factor;
LogicalSize::new(width, height).cast()
}
#[inline]
pub fn cast<X: Pixel>(&self) -> PhysicalSize<X> {
PhysicalSize {
width: self.width.cast(),
height: self.height.cast(),
}
}
}
impl<P: Pixel, X: Pixel> From<(X, X)> for PhysicalSize<P> {
fn from((x, y): (X, X)) -> PhysicalSize<P> {
PhysicalSize::new(x.cast(), y.cast())
}
}
impl<P: Pixel, X: Pixel> From<PhysicalSize<P>> for (X, X) {
fn from(size: PhysicalSize<P>) -> Self {
(size.width.cast(), size.height.cast())
}
}
impl<P: Pixel, X: Pixel> From<[X; 2]> for PhysicalSize<P> {
fn from([x, y]: [X; 2]) -> PhysicalSize<P> {
PhysicalSize::new(x.cast(), y.cast())
}
}
impl<P: Pixel, X: Pixel> From<PhysicalSize<P>> for [X; 2] {
fn from(size: PhysicalSize<P>) -> Self {
[size.width.cast(), size.height.cast()]
}
}
/// A size that's either physical or logical.
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type", content = "data")]
pub enum Size {
Physical(PhysicalSize<u32>),
Logical(LogicalSize<f64>),
}
impl Size {
pub fn new<S: Into<Size>>(size: S) -> Size {
size.into()
}
pub fn to_logical<P: Pixel>(&self, scale_factor: f64) -> LogicalSize<P> {
match *self {
Size::Physical(size) => size.to_logical(scale_factor),
Size::Logical(size) => size.cast(),
}
}
pub fn to_physical<P: Pixel>(&self, scale_factor: f64) -> PhysicalSize<P> {
match *self {
Size::Physical(size) => size.cast(),
Size::Logical(size) => size.to_physical(scale_factor),
}
}
}
impl<P: Pixel> From<PhysicalSize<P>> for Size {
#[inline]
fn from(size: PhysicalSize<P>) -> Size {
Size::Physical(size.cast())
}
}
impl<P: Pixel> From<LogicalSize<P>> for Size {
#[inline]
fn from(size: LogicalSize<P>) -> Size {
Size::Logical(size.cast())
}
}
/// A position that's either physical or logical.
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type", content = "data")]
pub enum Position {
Physical(PhysicalPosition<i32>),
Logical(LogicalPosition<f64>),
}
impl Position {
pub fn new<S: Into<Position>>(position: S) -> Position {
position.into()
}
pub fn to_logical<P: Pixel>(&self, scale_factor: f64) -> LogicalPosition<P> {
match *self {
Position::Physical(position) => position.to_logical(scale_factor),
Position::Logical(position) => position.cast(),
}
}
pub fn to_physical<P: Pixel>(&self, scale_factor: f64) -> PhysicalPosition<P> {
match *self {
Position::Physical(position) => position.cast(),
Position::Logical(position) => position.to_physical(scale_factor),
}
}
}
impl<P: Pixel> From<PhysicalPosition<P>> for Position {
#[inline]
fn from(position: PhysicalPosition<P>) -> Position {
Position::Physical(position.cast())
}
}
impl<P: Pixel> From<LogicalPosition<P>> for Position {
#[inline]
fn from(position: LogicalPosition<P>) -> Position {
Position::Logical(position.cast())
}
}

View File

@ -1104,11 +1104,11 @@ pub struct WindowConfig {
/// The user agent for the webview
#[serde(alias = "user-agent")]
pub user_agent: Option<String>,
/// Whether the file drop is enabled or not on the webview. By default it is enabled.
/// Whether the drag and drop is enabled or not on the webview. By default it is enabled.
///
/// Disabling it is required to use drag and drop on the frontend on Windows.
#[serde(default = "default_true", alias = "file-drop-enabled")]
pub file_drop_enabled: bool,
/// Disabling it is required to use HTML5 drag and drop on the frontend on Windows.
#[serde(default = "default_true", alias = "drag-drop-enabled")]
pub drag_drop_enabled: bool,
/// Whether or not the window starts centered or not.
#[serde(default)]
pub center: bool,
@ -1284,7 +1284,7 @@ impl Default for WindowConfig {
label: default_window_label(),
url: WebviewUrl::default(),
user_agent: None,
file_drop_enabled: true,
drag_drop_enabled: true,
center: false,
x: None,
y: None,
@ -2205,7 +2205,7 @@ mod build {
let label = str_lit(&self.label);
let url = &self.url;
let user_agent = opt_str_lit(self.user_agent.as_ref());
let file_drop_enabled = self.file_drop_enabled;
let drag_drop_enabled = self.drag_drop_enabled;
let center = self.center;
let x = opt_lit(self.x.as_ref());
let y = opt_lit(self.y.as_ref());
@ -2249,7 +2249,7 @@ mod build {
label,
url,
user_agent,
file_drop_enabled,
drag_drop_enabled,
center,
x,
y,

View File

@ -57,10 +57,10 @@ tauri-runtime-wry = { version = "2.0.0-beta.10", path = "../tauri-runtime-wry",
getrandom = "0.2"
serde_repr = "0.1"
state = "0.6"
http = "0.2"
http = "1.1"
dirs-next = "2.0"
percent-encoding = "2.3"
reqwest = { version = "0.11", default-features = false, features = [ "json", "stream" ] }
reqwest = { version = "0.12", default-features = false, features = [ "json", "stream" ] }
bytes = { version = "1", features = [ "serde" ] }
raw-window-handle = "0.6"
glob = "0.3"
@ -75,8 +75,8 @@ heck = "0.4"
log = "0.4"
[target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\", target_os = \"windows\", target_os = \"macos\"))".dependencies]
muda = { version = "0.12", default-features = false, features = [ "serde" ] }
tray-icon = { version = "0.12", default-features = false, features = [ "serde" ], optional = true }
muda = { version = "0.13", default-features = false, features = [ "serde" ] }
tray-icon = { version = "0.13", default-features = false, features = [ "serde" ], optional = true }
[target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies]
gtk = { version = "0.18", features = [ "v3_24" ] }
@ -89,11 +89,11 @@ objc = "0.2"
window-vibrancy = "0.5"
[target."cfg(windows)".dependencies]
webview2-com = "0.28"
webview2-com = "0.29"
window-vibrancy = "0.5"
[target."cfg(windows)".dependencies.windows]
version = "0.52"
version = "0.54"
features = [ "Win32_Foundation" ]
[target."cfg(target_os = \"android\")".dependencies]

File diff suppressed because one or more lines are too long

View File

@ -35,10 +35,8 @@ use tauri_macros::default_runtime;
#[cfg(desktop)]
use tauri_runtime::EventLoopProxy;
use tauri_runtime::{
window::{
dpi::{PhysicalPosition, PhysicalSize},
FileDropEvent,
},
dpi::{PhysicalPosition, PhysicalSize},
window::DragDropEvent,
RuntimeInitArgs,
};
use tauri_utils::PackageInfo;
@ -132,8 +130,8 @@ pub enum WindowEvent {
/// The window inner size.
new_inner_size: PhysicalSize<u32>,
},
/// An event associated with the file drop action.
FileDrop(FileDropEvent),
/// An event associated with the drag and drop action.
DragDrop(DragDropEvent),
/// The system window theme has changed. Only delivered if the window [`theme`](`crate::window::WindowBuilder#method.theme`) is `None`.
///
/// Applications might wish to react to this to change the theme of the content of the window when the system changes the window theme.
@ -161,7 +159,7 @@ impl From<RuntimeWindowEvent> for WindowEvent {
scale_factor,
new_inner_size,
},
RuntimeWindowEvent::FileDrop(event) => Self::FileDrop(event),
RuntimeWindowEvent::DragDrop(event) => Self::DragDrop(event),
RuntimeWindowEvent::ThemeChanged(theme) => Self::ThemeChanged(theme),
}
}
@ -171,14 +169,14 @@ impl From<RuntimeWindowEvent> for WindowEvent {
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum WebviewEvent {
/// An event associated with the file drop action.
FileDrop(FileDropEvent),
/// An event associated with the drag and drop action.
DragDrop(DragDropEvent),
}
impl From<RuntimeWebviewEvent> for WebviewEvent {
fn from(event: RuntimeWebviewEvent) -> Self {
match event {
RuntimeWebviewEvent::FileDrop(e) => Self::FileDrop(e),
RuntimeWebviewEvent::DragDrop(e) => Self::DragDrop(e),
}
}
}
@ -1392,7 +1390,7 @@ tauri::Builder::default()
/// ```
/// tauri::Builder::default()
/// .on_webview_event(|window, event| match event {
/// tauri::WebviewEvent::FileDrop(event) => {
/// tauri::WebviewEvent::DragDrop(event) => {
/// println!("{:?}", event);
/// }
/// _ => {}

View File

@ -11,8 +11,9 @@ use crate::{
};
use http::{
header::{ACCESS_CONTROL_ALLOW_HEADERS, ACCESS_CONTROL_ALLOW_ORIGIN, CONTENT_TYPE},
HeaderValue, Method, StatusCode,
HeaderValue, Method, Request, StatusCode,
};
use url::Url;
use super::{CallbackFn, InvokeBody, InvokeResponse};
@ -161,11 +162,16 @@ pub fn get<R: Runtime>(manager: Arc<AppManager<R>>, label: String) -> UriSchemeP
})
}
fn handle_ipc_message<R: Runtime>(message: String, manager: &AppManager<R>, label: &str) {
fn handle_ipc_message<R: Runtime>(request: Request<String>, manager: &AppManager<R>, label: &str) {
if let Some(webview) = manager.get_webview(label) {
#[cfg(feature = "tracing")]
let _span =
tracing::trace_span!("ipc::request", kind = "post-message", request = message).entered();
let _span = tracing::trace_span!(
"ipc::request",
kind = "post-message",
uri = request.uri().to_string(),
body = request.body()
)
.entered();
use serde::{Deserialize, Deserializer};
@ -227,7 +233,7 @@ fn handle_ipc_message<R: Runtime>(message: String, manager: &AppManager<R>, labe
let _span = tracing::trace_span!("ipc::request::decrypt_isolation_payload").entered();
invoke_message.replace(
serde_json::from_str::<IsolationMessage<'_>>(&message)
serde_json::from_str::<IsolationMessage<'_>>(request.body())
.map_err(Into::into)
.and_then(|message| {
Ok(Message {
@ -245,7 +251,7 @@ fn handle_ipc_message<R: Runtime>(message: String, manager: &AppManager<R>, labe
let message = invoke_message.unwrap_or_else(|| {
#[cfg(feature = "tracing")]
let _span = tracing::trace_span!("ipc::request::deserialize").entered();
serde_json::from_str::<Message>(&message).map_err(Into::into)
serde_json::from_str::<Message>(request.body()).map_err(Into::into)
});
match message {
@ -254,6 +260,7 @@ fn handle_ipc_message<R: Runtime>(message: String, manager: &AppManager<R>, labe
cmd: message.cmd,
callback: message.callback,
error: message.error,
url: Url::parse(&request.uri().to_string()).expect("invalid IPC request URL"),
body: message.payload.into(),
headers: message.options.map(|o| o.headers.0).unwrap_or_default(),
};
@ -389,6 +396,16 @@ fn parse_invoke_request<R: Runtime>(
}
}
let url = Url::parse(
parts
.headers
.get("Origin")
.ok_or("missing Origin header")?
.to_str()
.map_err(|_| "Origin header value must be a string")?,
)
.map_err(|_| "Origin header is not a valid URL")?;
let callback = CallbackFn(
parts
.headers
@ -412,7 +429,7 @@ fn parse_invoke_request<R: Runtime>(
let content_type = parts
.headers
.get(reqwest::header::CONTENT_TYPE)
.get(http::header::CONTENT_TYPE)
.and_then(|h| h.to_str().ok())
.map(|mime| mime.parse())
.unwrap_or(Ok(mime::APPLICATION_OCTET_STREAM))
@ -443,6 +460,7 @@ fn parse_invoke_request<R: Runtime>(
cmd,
callback,
error,
url,
body,
headers: parts.headers,
};

View File

@ -217,12 +217,10 @@ pub use {
},
self::manager::Asset,
self::runtime::{
dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Pixel, Position, Size},
webview::WebviewAttributes,
window::{
dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Pixel, Position, Size},
CursorIcon, FileDropEvent,
},
DeviceEventFilter, UserAttentionType,
window::{CursorIcon, DragDropEvent},
DeviceEventFilter, Rect, UserAttentionType,
},
self::state::{State, StateManager},
self::utils::{

View File

@ -14,7 +14,7 @@ use serde::Serialize;
use serialize_to_javascript::{default_template, DefaultTemplate, Template};
use tauri_runtime::{
webview::{DetachedWebview, PendingWebview},
window::FileDropEvent,
window::DragDropEvent,
};
use tauri_utils::config::WebviewUrl;
use url::Url;
@ -29,7 +29,10 @@ use crate::{
};
use super::{
window::{FileDropPayload, DROP_CANCELLED_EVENT, DROP_EVENT, DROP_HOVER_EVENT},
window::{
DragDropPayload, DragOverPayload, DRAG_EVENT, DROP_CANCELLED_EVENT, DROP_EVENT,
DROP_HOVER_EVENT,
},
AppManager,
};
@ -654,12 +657,16 @@ impl<R: Runtime> Webview<R> {
fn on_webview_event<R: Runtime>(webview: &Webview<R>, event: &WebviewEvent) -> crate::Result<()> {
match event {
WebviewEvent::FileDrop(event) => match event {
FileDropEvent::Hovered { paths, position } => {
let payload = FileDropPayload { paths, position };
WebviewEvent::DragDrop(event) => match event {
DragDropEvent::Dragged { paths, position } => {
let payload = DragDropPayload { paths, position };
webview.emit_to_webview(DRAG_EVENT, payload)?
}
DragDropEvent::DragOver { position } => {
let payload = DragOverPayload { position };
webview.emit_to_webview(DROP_HOVER_EVENT, payload)?
}
FileDropEvent::Dropped { paths, position } => {
DragDropEvent::Dropped { paths, position } => {
let scopes = webview.state::<Scopes>();
for path in paths {
if path.is_file() {
@ -668,10 +675,10 @@ fn on_webview_event<R: Runtime>(webview: &Webview<R>, event: &WebviewEvent) -> c
let _ = scopes.allow_directory(path, false);
}
}
let payload = FileDropPayload { paths, position };
let payload = DragDropPayload { paths, position };
webview.emit_to_webview(DROP_EVENT, payload)?
}
FileDropEvent::Cancelled => webview.emit_to_webview(DROP_CANCELLED_EVENT, ())?,
DragDropEvent::Cancelled => webview.emit_to_webview(DROP_CANCELLED_EVENT, ())?,
_ => unimplemented!(),
},
}

View File

@ -11,11 +11,9 @@ use std::{
use serde::Serialize;
use tauri_runtime::{
dpi::{PhysicalPosition, PhysicalSize},
window::WindowBuilder,
window::{
dpi::{PhysicalPosition, PhysicalSize},
DetachedWindow, FileDropEvent, PendingWindow,
},
window::{DetachedWindow, DragDropEvent, PendingWindow},
};
use crate::{
@ -31,9 +29,10 @@ const WINDOW_FOCUS_EVENT: &str = "tauri://focus";
const WINDOW_BLUR_EVENT: &str = "tauri://blur";
const WINDOW_SCALE_FACTOR_CHANGED_EVENT: &str = "tauri://scale-change";
const WINDOW_THEME_CHANGED: &str = "tauri://theme-changed";
pub const DROP_EVENT: &str = "tauri://file-drop";
pub const DROP_HOVER_EVENT: &str = "tauri://file-drop-hover";
pub const DROP_CANCELLED_EVENT: &str = "tauri://file-drop-cancelled";
pub const DRAG_EVENT: &str = "tauri://drag";
pub const DROP_EVENT: &str = "tauri://drop";
pub const DROP_HOVER_EVENT: &str = "tauri://drop-over";
pub const DROP_CANCELLED_EVENT: &str = "tauri://drag-cancelled";
pub struct WindowManager<R: Runtime> {
pub windows: Mutex<HashMap<String, Window<R>>>,
@ -145,11 +144,16 @@ impl<R: Runtime> Window<R> {
}
#[derive(Serialize, Clone)]
pub struct FileDropPayload<'a> {
pub struct DragDropPayload<'a> {
pub paths: &'a Vec<PathBuf>,
pub position: &'a PhysicalPosition<f64>,
}
#[derive(Serialize, Clone)]
pub struct DragOverPayload<'a> {
pub position: &'a PhysicalPosition<f64>,
}
fn on_window_event<R: Runtime>(window: &Window<R>, event: &WindowEvent) -> crate::Result<()> {
match event {
WindowEvent::Resized(size) => window.emit_to_window(WINDOW_RESIZED_EVENT, size)?,
@ -190,9 +194,18 @@ fn on_window_event<R: Runtime>(window: &Window<R>, event: &WindowEvent) -> crate
size: *new_inner_size,
},
)?,
WindowEvent::FileDrop(event) => match event {
FileDropEvent::Hovered { paths, position } => {
let payload = FileDropPayload { paths, position };
WindowEvent::DragDrop(event) => match event {
DragDropEvent::Dragged { paths, position } => {
let payload = DragDropPayload { paths, position };
if window.is_webview_window() {
window.emit_to(EventTarget::labeled(window.label()), DRAG_EVENT, payload)?
} else {
window.emit_to_window(DRAG_EVENT, payload)?
}
}
DragDropEvent::DragOver { position } => {
let payload = DragOverPayload { position };
if window.is_webview_window() {
window.emit_to(
EventTarget::labeled(window.label()),
@ -203,7 +216,7 @@ fn on_window_event<R: Runtime>(window: &Window<R>, event: &WindowEvent) -> crate
window.emit_to_window(DROP_HOVER_EVENT, payload)?
}
}
FileDropEvent::Dropped { paths, position } => {
DragDropEvent::Dropped { paths, position } => {
let scopes = window.state::<Scopes>();
for path in paths {
if path.is_file() {
@ -212,7 +225,7 @@ fn on_window_event<R: Runtime>(window: &Window<R>, event: &WindowEvent) -> crate
let _ = scopes.allow_directory(path, true);
}
}
let payload = FileDropPayload { paths, position };
let payload = DragDropPayload { paths, position };
if window.is_webview_window() {
window.emit_to(EventTarget::labeled(window.label()), DROP_EVENT, payload)?
@ -220,7 +233,7 @@ fn on_window_event<R: Runtime>(window: &Window<R>, event: &WindowEvent) -> crate
window.emit_to_window(DROP_EVENT, payload)?
}
}
FileDropEvent::Cancelled => {
DragDropEvent::Cancelled => {
if window.is_webview_window() {
window.emit_to(
EventTarget::labeled(window.label()),

View File

@ -40,7 +40,7 @@ impl<R: Runtime> ContextMenuBase for Menu<R> {
window: crate::Window<T>,
position: Option<P>,
) -> crate::Result<()> {
let position = position.map(Into::into).map(super::into_position);
let position = position.map(Into::into);
run_item_main_thread!(self, move |self_: Self| {
#[cfg(target_os = "macos")]
if let Ok(view) = window.ns_view() {

View File

@ -762,22 +762,3 @@ pub(crate) mod sealed {
) -> crate::Result<()>;
}
}
pub(crate) fn into_logical_position<P: crate::Pixel>(
p: crate::LogicalPosition<P>,
) -> muda::LogicalPosition<P> {
muda::LogicalPosition { x: p.x, y: p.y }
}
pub(crate) fn into_physical_position<P: crate::Pixel>(
p: crate::PhysicalPosition<P>,
) -> muda::PhysicalPosition<P> {
muda::PhysicalPosition { x: p.x, y: p.y }
}
pub(crate) fn into_position(p: crate::Position) -> muda::Position {
match p {
crate::Position::Physical(p) => muda::Position::Physical(into_physical_position(p)),
crate::Position::Logical(p) => muda::Position::Logical(into_logical_position(p)),
}
}

View File

@ -8,7 +8,7 @@ use std::{
};
use serde::{Deserialize, Serialize};
use tauri_runtime::window::dpi::Position;
use tauri_runtime::dpi::Position;
use super::{sealed::ContextMenuBase, *};
use crate::{

View File

@ -32,7 +32,7 @@ impl<R: Runtime> ContextMenuBase for Submenu<R> {
window: crate::Window<T>,
position: Option<P>,
) -> crate::Result<()> {
let position = position.map(Into::into).map(super::into_position);
let position = position.map(Into::into);
run_item_main_thread!(self, move |self_: Self| {
#[cfg(target_os = "macos")]
if let Ok(view) = window.ns_view() {

View File

@ -6,12 +6,10 @@
#![allow(missing_docs)]
use tauri_runtime::{
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
monitor::Monitor,
webview::{DetachedWebview, PendingWebview},
window::{
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
CursorIcon, DetachedWindow, PendingWindow, RawWindow, WindowEvent, WindowId,
},
window::{CursorIcon, DetachedWindow, PendingWindow, RawWindow, WindowEvent, WindowId},
window::{WindowBuilder, WindowBuilderBase},
DeviceEventFilter, Error, EventLoopProxy, ExitRequestedEventAction, Icon, ProgressBarState,
Result, RunEvent, Runtime, RuntimeHandle, RuntimeInitArgs, UserAttentionType, UserEvent,
@ -505,6 +503,10 @@ impl<T: UserEvent> WebviewDispatch<T> for MockWebviewDispatcher {
.map_err(|_| Error::FailedToReceiveMessage)
}
fn bounds(&self) -> Result<tauri_runtime::Rect> {
Ok(tauri_runtime::Rect::default())
}
fn position(&self) -> Result<PhysicalPosition<i32>> {
Ok(PhysicalPosition { x: 0, y: 0 })
}
@ -529,6 +531,10 @@ impl<T: UserEvent> WebviewDispatch<T> for MockWebviewDispatcher {
Ok(())
}
fn set_bounds(&self, bounds: tauri_runtime::Rect) -> Result<()> {
Ok(())
}
fn set_size(&self, _size: Size) -> Result<()> {
Ok(())
}

View File

@ -39,6 +39,7 @@
//! cmd: "ping".into(),
//! callback: tauri::ipc::CallbackFn(0),
//! error: tauri::ipc::CallbackFn(1),
//! url: "http://tauri.localhost".parse().unwrap(),
//! body: tauri::ipc::InvokeBody::default(),
//! headers: Default::default(),
//! },
@ -186,6 +187,7 @@ pub fn mock_app() -> App<MockRuntime> {
/// cmd: "ping".into(),
/// callback: tauri::ipc::CallbackFn(0),
/// error: tauri::ipc::CallbackFn(1),
/// url: "http://tauri.localhost".parse().unwrap(),
/// body: tauri::ipc::InvokeBody::default(),
/// headers: Default::default(),
/// },
@ -242,6 +244,7 @@ pub fn assert_ipc_response<
/// cmd: "ping".into(),
/// callback: tauri::ipc::CallbackFn(0),
/// error: tauri::ipc::CallbackFn(1),
/// url: "http://tauri.localhost".parse().unwrap(),
/// body: tauri::ipc::InvokeBody::default(),
/// headers: Default::default(),
/// },

View File

@ -12,24 +12,13 @@ use crate::app::{GlobalMenuEventListener, GlobalTrayIconEventListener};
use crate::menu::ContextMenu;
use crate::menu::MenuEvent;
use crate::resources::Resource;
use crate::{image::Image, menu::run_item_main_thread, AppHandle, Manager, Runtime};
use crate::{
image::Image, menu::run_item_main_thread, AppHandle, Manager, PhysicalPosition, Rect, Runtime,
};
use serde::Serialize;
use std::path::Path;
pub use tray_icon::TrayIconId;
/// Describes a rectangle including position (x - y axis) and size.
#[derive(Debug, PartialEq, Clone, Copy, Default, Serialize)]
pub struct Rectangle {
/// The x-coordinate of the upper-left corner of the rectangle.
pub left: f64,
/// The y-coordinate of the upper-left corner of the rectangle.
pub top: f64,
/// The x-coordinate of the lower-right corner of the rectangle.
pub right: f64,
/// The y-coordinate of the lower-right corner of the rectangle.
pub bottom: f64,
}
/// Describes the click type that triggered this tray icon event.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Serialize)]
pub enum ClickType {
@ -58,12 +47,10 @@ impl Default for ClickType {
pub struct TrayIconEvent {
/// Id of the tray icon which triggered this event.
pub id: TrayIconId,
/// Physical X Position of the click the triggered this event.
pub x: f64,
/// Physical Y Position of the click the triggered this event.
pub y: f64,
/// Physical Position of the click the triggered this event.
pub position: PhysicalPosition<f64>,
/// Position and size of the tray icon
pub icon_rect: Rectangle,
pub icon_rect: Rect,
/// The click type that triggered this event.
pub click_type: ClickType,
}
@ -75,17 +62,6 @@ impl TrayIconEvent {
}
}
impl From<tray_icon::Rectangle> for Rectangle {
fn from(value: tray_icon::Rectangle) -> Self {
Self {
bottom: value.bottom,
left: value.left,
top: value.top,
right: value.right,
}
}
}
impl From<tray_icon::ClickType> for ClickType {
fn from(value: tray_icon::ClickType) -> Self {
match value {
@ -100,9 +76,11 @@ impl From<tray_icon::TrayIconEvent> for TrayIconEvent {
fn from(value: tray_icon::TrayIconEvent) -> Self {
Self {
id: value.id,
x: value.x,
y: value.y,
icon_rect: value.icon_rect.into(),
position: value.position,
icon_rect: Rect {
position: value.icon_rect.position.into(),
size: value.icon_rect.size.into(),
},
click_type: value.click_type.into(),
}
}

View File

@ -13,15 +13,15 @@ use http::HeaderMap;
use serde::Serialize;
use tauri_macros::default_runtime;
pub use tauri_runtime::webview::PageLoadEvent;
use tauri_runtime::{
webview::{DetachedWebview, PendingWebview, WebviewAttributes},
WebviewDispatch,
};
#[cfg(desktop)]
use tauri_runtime::{
window::dpi::{PhysicalPosition, PhysicalSize, Position, Size},
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
WindowDispatch,
};
use tauri_runtime::{
webview::{DetachedWebview, PendingWebview, WebviewAttributes},
Rect, WebviewDispatch,
};
use tauri_utils::config::{WebviewUrl, WindowConfig};
pub use url::Url;
@ -118,6 +118,8 @@ pub struct InvokeRequest {
pub callback: CallbackFn,
/// The error callback.
pub error: CallbackFn,
/// URL of the frame that requested this command.
pub url: Url,
/// The body of the request.
pub body: InvokeBody,
/// The request headers.
@ -616,7 +618,7 @@ tauri::Builder::default()
let mut pending =
self.into_pending_webview(&window, window.label(), &window_labels, &webview_labels)?;
pending.webview_attributes.bounds = Some((position, size));
pending.webview_attributes.bounds = Some(Rect { size, position });
let webview = match &mut window.runtime() {
RuntimeOrDispatch::Dispatch(dispatcher) => dispatcher.create_webview(pending),
@ -714,10 +716,10 @@ fn main() {
self
}
/// Disables the file drop handler. This is required to use drag and drop APIs on the front end on Windows.
/// Disables the drag and drop handler. This is required to use HTML5 drag and drop APIs on the frontend on Windows.
#[must_use]
pub fn disable_file_drop_handler(mut self) -> Self {
self.webview_attributes.file_drop_handler_enabled = false;
pub fn disable_drag_drop_handler(mut self) -> Self {
self.webview_attributes.drag_drop_handler_enabled = false;
self
}
@ -882,6 +884,15 @@ impl<R: Runtime> Webview<R> {
Ok(())
}
/// Resizes this webview.
pub fn set_bounds(&self, bounds: Rect) -> crate::Result<()> {
self
.webview
.dispatcher
.set_bounds(bounds)
.map_err(Into::into)
}
/// Resizes this webview.
pub fn set_size<S: Into<Size>>(&self, size: S) -> crate::Result<()> {
self
@ -929,6 +940,11 @@ impl<R: Runtime> Webview<R> {
.map_err(Into::into)
}
/// Returns the bounds of the webviews's client area.
pub fn bounds(&self) -> crate::Result<Rect> {
self.webview.dispatcher.bounds().map_err(Into::into)
}
/// Returns the webview position.
///
/// - For child webviews, returns the position of the top-left hand corner of the webviews's client area relative to the top-left hand corner of the parent window.
@ -1081,8 +1097,7 @@ fn main() {
/// Handles this window receiving an [`InvokeRequest`].
pub fn on_message(self, request: InvokeRequest, responder: Box<OwnedInvokeResponder<R>>) {
let manager = self.manager_owned();
let current_url = self.url();
let is_local = self.is_local_url(&current_url);
let is_local = self.is_local_url(&request.url);
let custom_responder = self.manager().webview.invoke_responder.clone();
@ -1118,7 +1133,7 @@ fn main() {
Origin::Local
} else {
Origin::Remote {
url: current_url.clone(),
url: request.url.clone(),
}
};
let (resolved_acl, has_app_acl_manifest) = {

View File

@ -13,7 +13,7 @@ use crate::{
mod desktop_commands {
use serde::Deserialize;
use tauri_runtime::window::dpi::{Position, Size};
use tauri_runtime::dpi::{Position, Size};
use tauri_utils::config::{WebviewUrl, WindowConfig};
use super::*;
@ -28,7 +28,7 @@ mod desktop_commands {
#[serde(default)]
url: WebviewUrl,
user_agent: Option<String>,
file_drop_enabled: Option<bool>,
drag_drop_enabled: Option<bool>,
x: f64,
y: f64,
width: f64,
@ -71,8 +71,8 @@ mod desktop_commands {
let mut builder = crate::webview::WebviewBuilder::new(label, options.url);
builder.webview_attributes.user_agent = options.user_agent;
builder.webview_attributes.file_drop_handler_enabled =
options.file_drop_enabled.unwrap_or(true);
builder.webview_attributes.drag_drop_handler_enabled =
options.drag_drop_enabled.unwrap_or(true);
builder.webview_attributes.transparent = options.transparent;
builder.webview_attributes.accept_first_mouse = options.accept_first_mouse;
builder.webview_attributes.window_effects = options.window_effects;
@ -80,8 +80,8 @@ mod desktop_commands {
window.add_child(
builder,
tauri_runtime::window::dpi::LogicalPosition::new(options.x, options.y),
tauri_runtime::window::dpi::LogicalSize::new(options.width, options.height),
tauri_runtime::dpi::LogicalPosition::new(options.x, options.y),
tauri_runtime::dpi::LogicalSize::new(options.width, options.height),
)?;
Ok(())
@ -144,13 +144,9 @@ mod desktop_commands {
getter!(
webview_position,
position,
tauri_runtime::window::dpi::PhysicalPosition<i32>
);
getter!(
webview_size,
size,
tauri_runtime::window::dpi::PhysicalSize<u32>
tauri_runtime::dpi::PhysicalPosition<i32>
);
getter!(webview_size, size, tauri_runtime::dpi::PhysicalSize<u32>);
//getter!(is_focused, bool);
setter!(print);

View File

@ -8,7 +8,7 @@ use std::{borrow::Cow, path::PathBuf, sync::Arc};
use crate::{
event::EventTarget,
runtime::window::dpi::{PhysicalPosition, PhysicalSize},
runtime::dpi::{PhysicalPosition, PhysicalSize},
window::Monitor,
};
#[cfg(desktop)]
@ -16,10 +16,8 @@ use crate::{
image::Image,
menu::{ContextMenu, Menu},
runtime::{
window::{
dpi::{Position, Size},
CursorIcon,
},
dpi::{Position, Size},
window::CursorIcon,
UserAttentionType,
},
};
@ -792,10 +790,10 @@ fn main() {
self
}
/// Disables the file drop handler. This is required to use drag and drop APIs on the front end on Windows.
/// Disables the drag and drop handler. This is required to use HTML5 drag and drop APIs on the frontend on Windows.
#[must_use]
pub fn disable_file_drop_handler(mut self) -> Self {
self.webview_builder = self.webview_builder.disable_file_drop_handler();
pub fn disable_drag_drop_handler(mut self) -> Self {
self.webview_builder = self.webview_builder.disable_drag_drop_handler();
self
}

View File

@ -7,8 +7,8 @@
pub(crate) mod plugin;
use tauri_runtime::{
dpi::{PhysicalPosition, PhysicalSize},
webview::PendingWebview,
window::dpi::{PhysicalPosition, PhysicalSize},
};
pub use tauri_utils::{config::Color, WindowEffect as Effect, WindowEffectState as EffectState};
@ -36,7 +36,7 @@ use crate::{
image::Image,
menu::{ContextMenu, Menu, MenuId},
runtime::{
window::dpi::{Position, Size},
dpi::{Position, Size},
UserAttentionType,
},
CursorIcon,

View File

@ -110,12 +110,6 @@ dependencies = [
"tiny_http",
]
[[package]]
name = "as-raw-xcb-connection"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b"
[[package]]
name = "ascii"
version = "1.1.0"
@ -240,20 +234,6 @@ name = "bytemuck"
version = "1.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2ef034f05691a48569bd920a96c81b9d91bbad1ab5ac7c4616c1f6ef36cb79f"
dependencies = [
"bytemuck_derive",
]
[[package]]
name = "bytemuck_derive"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.52",
]
[[package]]
name = "byteorder"
@ -726,51 +706,6 @@ dependencies = [
"syn 2.0.52",
]
[[package]]
name = "downcast-rs"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
[[package]]
name = "drm"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0f8a69e60d75ae7dab4ef26a59ca99f2a89d4c142089b537775ae0c198bdcde"
dependencies = [
"bitflags 2.4.2",
"bytemuck",
"drm-ffi",
"drm-fourcc",
"rustix",
]
[[package]]
name = "drm-ffi"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41334f8405792483e32ad05fbb9c5680ff4e84491883d2947a4757dc54cb2ac6"
dependencies = [
"drm-sys",
"rustix",
]
[[package]]
name = "drm-fourcc"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0aafbcdb8afc29c1a7ee5fbe53b5d62f4565b35a042a662ca9fecd0b54dae6f4"
[[package]]
name = "drm-sys"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d09ff881f92f118b11105ba5e34ff8f4adf27b30dae8f12e28c193af1c83176"
dependencies = [
"libc",
"linux-raw-sys 0.6.4",
]
[[package]]
name = "dtoa"
version = "1.0.9"
@ -833,22 +768,6 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "errno"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
dependencies = [
"libc",
"windows-sys 0.52.0",
]
[[package]]
name = "fastrand"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
[[package]]
name = "fdeflate"
version = "0.3.4"
@ -1134,16 +1053,6 @@ dependencies = [
"version_check",
]
[[package]]
name = "gethostname"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818"
dependencies = [
"libc",
"windows-targets 0.48.5",
]
[[package]]
name = "getrandom"
version = "0.1.16"
@ -1752,18 +1661,6 @@ dependencies = [
"safemem",
]
[[package]]
name = "linux-raw-sys"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
[[package]]
name = "linux-raw-sys"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0b5399f6804fbab912acbd8878ed3532d506b7c951b8f9f164ef90fef39e3f4"
[[package]]
name = "lock_api"
version = "0.4.11"
@ -1845,15 +1742,6 @@ version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
[[package]]
name = "memmap2"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322"
dependencies = [
"libc",
]
[[package]]
name = "memoffset"
version = "0.9.0"
@ -1892,9 +1780,9 @@ dependencies = [
[[package]]
name = "muda"
version = "0.11.5"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c47e7625990fc1af2226ea4f34fb2412b03c12639fcb91868581eb3a6893453"
checksum = "3e27c56b8cb9b3214d196556227b0eaa12db8393b4f919a0a93ffb67ed17d185"
dependencies = [
"cocoa",
"crossbeam-channel",
@ -2610,19 +2498,6 @@ dependencies = [
"semver",
]
[[package]]
name = "rustix"
version = "0.38.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949"
dependencies = [
"bitflags 2.4.2",
"errno",
"libc",
"linux-raw-sys 0.4.13",
"windows-sys 0.52.0",
]
[[package]]
name = "rustversion"
version = "1.0.14"
@ -2916,29 +2791,20 @@ version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071916a85d1db274b4ed57af3a14afb66bd836ae7f82ebb6f1fd3455107830d9"
dependencies = [
"as-raw-xcb-connection",
"bytemuck",
"cfg_aliases 0.2.0",
"cocoa",
"core-graphics",
"drm",
"fastrand",
"foreign-types",
"js-sys",
"log",
"memmap2",
"objc",
"raw-window-handle 0.6.0",
"redox_syscall",
"rustix",
"tiny-xlib",
"wasm-bindgen",
"wayland-backend",
"wayland-client",
"wayland-sys",
"web-sys",
"windows-sys 0.52.0",
"x11rb",
]
[[package]]
@ -3128,7 +2994,7 @@ dependencies = [
"unicode-segmentation",
"url",
"windows 0.52.0",
"windows-implement",
"windows-implement 0.52.0",
"windows-version",
"x11-dl",
]
@ -3152,7 +3018,7 @@ checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f"
[[package]]
name = "tauri"
version = "2.0.0-beta.11"
version = "2.0.0-beta.12"
dependencies = [
"anyhow",
"bytes",
@ -3196,12 +3062,12 @@ dependencies = [
"webkit2gtk",
"webview2-com",
"window-vibrancy",
"windows 0.52.0",
"windows 0.54.0",
]
[[package]]
name = "tauri-build"
version = "2.0.0-beta.9"
version = "2.0.0-beta.10"
dependencies = [
"anyhow",
"cargo_toml",
@ -3223,7 +3089,7 @@ dependencies = [
[[package]]
name = "tauri-codegen"
version = "2.0.0-beta.9"
version = "2.0.0-beta.10"
dependencies = [
"base64 0.22.0",
"brotli",
@ -3248,7 +3114,7 @@ dependencies = [
[[package]]
name = "tauri-macros"
version = "2.0.0-beta.9"
version = "2.0.0-beta.10"
dependencies = [
"heck",
"proc-macro2",
@ -3260,7 +3126,7 @@ dependencies = [
[[package]]
name = "tauri-plugin"
version = "2.0.0-beta.9"
version = "2.0.0-beta.10"
dependencies = [
"anyhow",
"glob",
@ -3286,7 +3152,7 @@ dependencies = [
[[package]]
name = "tauri-runtime"
version = "2.0.0-beta.9"
version = "2.0.0-beta.10"
dependencies = [
"gtk",
"http",
@ -3297,12 +3163,12 @@ dependencies = [
"tauri-utils",
"thiserror",
"url",
"windows 0.52.0",
"windows 0.54.0",
]
[[package]]
name = "tauri-runtime-wry"
version = "2.0.0-beta.9"
version = "2.0.0-beta.10"
dependencies = [
"cocoa",
"gtk",
@ -3318,13 +3184,13 @@ dependencies = [
"url",
"webkit2gtk",
"webview2-com",
"windows 0.52.0",
"windows 0.54.0",
"wry",
]
[[package]]
name = "tauri-utils"
version = "2.0.0-beta.9"
version = "2.0.0-beta.10"
dependencies = [
"aes-gcm",
"brotli",
@ -3446,18 +3312,6 @@ dependencies = [
"time-core",
]
[[package]]
name = "tiny-xlib"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4098d49269baa034a8d1eae9bd63e9fa532148d772121dace3bcd6a6c98eb6d"
dependencies = [
"as-raw-xcb-connection",
"ctor",
"libloading 0.8.1",
"tracing",
]
[[package]]
name = "tiny_http"
version = "0.11.0"
@ -3644,9 +3498,9 @@ dependencies = [
[[package]]
name = "tray-icon"
version = "0.11.3"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a4d9ddd4a7c0f3b6862af1c4911b529a49db4ee89310d3a258859c2f5053fdd"
checksum = "454035ff34b8430638c894e6197748578d6b4d449c6edaf8ea854d94e2dd862b"
dependencies = [
"cocoa",
"core-graphics",
@ -3949,43 +3803,6 @@ dependencies = [
"web-sys",
]
[[package]]
name = "wayland-backend"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d50fa61ce90d76474c87f5fc002828d81b32677340112b4ef08079a9d459a40"
dependencies = [
"cc",
"downcast-rs",
"rustix",
"scoped-tls",
"smallvec",
"wayland-sys",
]
[[package]]
name = "wayland-client"
version = "0.31.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82fb96ee935c2cea6668ccb470fb7771f6215d1691746c2d896b447a00ad3f1f"
dependencies = [
"bitflags 2.4.2",
"rustix",
"wayland-backend",
"wayland-scanner",
]
[[package]]
name = "wayland-scanner"
version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63b3a62929287001986fb58c789dce9b67604a397c15c611ad9f747300b6c283"
dependencies = [
"proc-macro2",
"quick-xml",
"quote",
]
[[package]]
name = "wayland-sys"
version = "0.31.1"
@ -3994,7 +3811,6 @@ checksum = "15a0c8eaff5216d07f226cb7a549159267f3467b289d9a2e52fd3ef5aae2b7af"
dependencies = [
"dlib",
"log",
"once_cell",
"pkg-config",
]
@ -4054,16 +3870,16 @@ dependencies = [
[[package]]
name = "webview2-com"
version = "0.28.0"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0ae9c7e420783826cf769d2c06ac9ba462f450eca5893bb8c6c6529a4e5dd33"
checksum = "38d5949fc3f537e90240c3e4f78dda2fa0431b671d50845a2f582173ef8a1201"
dependencies = [
"webview2-com-macros",
"webview2-com-sys",
"windows 0.52.0",
"windows-core 0.52.0",
"windows-implement",
"windows-interface",
"windows 0.54.0",
"windows-core 0.54.0",
"windows-implement 0.53.0",
"windows-interface 0.53.0",
]
[[package]]
@ -4079,13 +3895,13 @@ dependencies = [
[[package]]
name = "webview2-com-sys"
version = "0.28.0"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6ad85fceee6c42fa3d61239eba5a11401bf38407a849ed5ea1b407df08cca72"
checksum = "cd1eaa1be63d6fdcadf893c40d7d53c889a6342b3a94930d34e6964d5bb7e8db"
dependencies = [
"thiserror",
"windows 0.52.0",
"windows-core 0.52.0",
"windows 0.54.0",
"windows-core 0.54.0",
]
[[package]]
@ -4139,8 +3955,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
dependencies = [
"windows-core 0.52.0",
"windows-implement",
"windows-interface",
"windows-implement 0.52.0",
"windows-interface 0.52.0",
"windows-targets 0.52.4",
]
@ -4154,6 +3970,18 @@ dependencies = [
"windows-targets 0.52.4",
]
[[package]]
name = "windows"
version = "0.54.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49"
dependencies = [
"windows-core 0.54.0",
"windows-implement 0.53.0",
"windows-interface 0.53.0",
"windows-targets 0.52.4",
]
[[package]]
name = "windows-core"
version = "0.52.0"
@ -4173,6 +4001,16 @@ dependencies = [
"windows-targets 0.52.4",
]
[[package]]
name = "windows-core"
version = "0.54.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65"
dependencies = [
"windows-result",
"windows-targets 0.52.4",
]
[[package]]
name = "windows-implement"
version = "0.52.0"
@ -4184,6 +4022,17 @@ dependencies = [
"syn 2.0.52",
]
[[package]]
name = "windows-implement"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "942ac266be9249c84ca862f0a164a39533dc2f6f33dc98ec89c8da99b82ea0bd"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.52",
]
[[package]]
name = "windows-interface"
version = "0.52.0"
@ -4195,6 +4044,17 @@ dependencies = [
"syn 2.0.52",
]
[[package]]
name = "windows-interface"
version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da33557140a288fae4e1d5f8873aaf9eb6613a9cf82c3e070223ff177f598b60"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.52",
]
[[package]]
name = "windows-result"
version = "0.1.0"
@ -4442,9 +4302,9 @@ dependencies = [
[[package]]
name = "wry"
version = "0.37.0"
version = "0.38.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b717040ba9771fd88eb428c6ea6b555f8e734ff8534f02c13e8f10d97f5935e"
checksum = "741261f2571990873d0dd5953cff8087f39a16fb37550edd0acf7221b9c8d65b"
dependencies = [
"base64 0.21.7",
"block",
@ -4461,7 +4321,6 @@ dependencies = [
"jni",
"kuchikiki",
"libc",
"log",
"ndk",
"ndk-context",
"ndk-sys",
@ -4470,8 +4329,6 @@ dependencies = [
"once_cell",
"percent-encoding",
"raw-window-handle 0.6.0",
"serde",
"serde_json",
"sha2",
"soup3",
"tao-macros",
@ -4479,8 +4336,7 @@ dependencies = [
"webkit2gtk",
"webkit2gtk-sys",
"webview2-com",
"windows 0.52.0",
"windows-implement",
"windows 0.54.0",
"windows-version",
"x11-dl",
]
@ -4505,24 +4361,3 @@ dependencies = [
"once_cell",
"pkg-config",
]
[[package]]
name = "x11rb"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8f25ead8c7e4cba123243a6367da5d3990e0d3affa708ea19dce96356bd9f1a"
dependencies = [
"as-raw-xcb-connection",
"gethostname",
"libc",
"libloading 0.8.1",
"once_cell",
"rustix",
"x11rb-protocol",
]
[[package]]
name = "x11rb-protocol"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e63e71c4b8bd9ffec2c963173a4dc4cbde9ee96961d4fcb4429db9929b606c34"

View File

@ -57,9 +57,10 @@ enum TauriEvent {
WINDOW_THEME_CHANGED = 'tauri://theme-changed',
WINDOW_CREATED = 'tauri://window-created',
WEBVIEW_CREATED = 'tauri://webview-created',
FILE_DROP = 'tauri://file-drop',
FILE_DROP_HOVER = 'tauri://file-drop-hover',
FILE_DROP_CANCELLED = 'tauri://file-drop-cancelled'
DRAG = 'tauri://drag',
DROP = 'tauri://drop',
DROP_OVER = 'tauri://drop-over',
DROP_CANCELLED = 'tauri://drag-cancelled'
}
/**

View File

@ -33,16 +33,21 @@ import { invoke } from './core'
import { Window, getCurrent as getCurrentWindow } from './window'
import { WebviewWindow } from './webviewWindow'
interface FileDropPayload {
interface DragDropPayload {
paths: string[]
position: PhysicalPosition
}
/** The file drop event types. */
type FileDropEvent =
| ({ type: 'hover' } & FileDropPayload)
| ({ type: 'drop' } & FileDropPayload)
| { type: 'cancel' }
interface DragOverPayload {
position: PhysicalPosition
}
/** The drag and drop event types. */
type DragDropEvent =
| ({ type: 'dragged' } & DragDropPayload)
| ({ type: 'dragOver' } & DragOverPayload)
| ({ type: 'dropped' } & DragDropPayload)
| { type: 'cancelled' }
/**
* Get an instance of `Webview` for the current webview.
@ -502,7 +507,7 @@ class Webview {
* @example
* ```typescript
* import { getCurrent } from "@tauri-apps/api/webview";
* const unlisten = await getCurrent().onFileDropEvent((event) => {
* const unlisten = await getCurrent().onDragDropEvent((event) => {
* if (event.payload.type === 'hover') {
* console.log('User hovering', event.payload.paths);
* } else if (event.payload.type === 'drop') {
@ -519,16 +524,16 @@ class Webview {
* @returns A promise resolving to a function to unlisten to the event.
* Note that removing the listener is required if your listener goes out of scope e.g. the component is unmounted.
*/
async onFileDropEvent(
handler: EventCallback<FileDropEvent>
async onDragDropEvent(
handler: EventCallback<DragDropEvent>
): Promise<UnlistenFn> {
const unlistenFileDrop = await this.listen<FileDropPayload>(
TauriEvent.FILE_DROP,
const unlistenDrag = await this.listen<DragDropPayload>(
TauriEvent.DRAG,
(event) => {
handler({
...event,
payload: {
type: 'drop',
type: 'dragged',
paths: event.payload.paths,
position: mapPhysicalPosition(event.payload.position)
}
@ -536,13 +541,13 @@ class Webview {
}
)
const unlistenFileHover = await this.listen<FileDropPayload>(
TauriEvent.FILE_DROP_HOVER,
const unlistenDrop = await this.listen<DragDropPayload>(
TauriEvent.DROP,
(event) => {
handler({
...event,
payload: {
type: 'hover',
type: 'dropped',
paths: event.payload.paths,
position: mapPhysicalPosition(event.payload.position)
}
@ -550,16 +555,30 @@ class Webview {
}
)
const unlistenDragOver = await this.listen<DragDropPayload>(
TauriEvent.DROP_CANCELLED,
(event) => {
handler({
...event,
payload: {
type: 'dragOver',
position: mapPhysicalPosition(event.payload.position)
}
})
}
)
const unlistenCancel = await this.listen<null>(
TauriEvent.FILE_DROP_CANCELLED,
TauriEvent.DROP_CANCELLED,
(event) => {
handler({ ...event, payload: { type: 'cancel' } })
handler({ ...event, payload: { type: 'cancelled' } })
}
)
return () => {
unlistenFileDrop()
unlistenFileHover()
unlistenDrag()
unlistenDrop()
unlistenDragOver()
unlistenCancel()
}
}
@ -598,11 +617,11 @@ interface WebviewOptions {
*/
transparent?: boolean
/**
* Whether the file drop is enabled or not on the webview. By default it is enabled.
* Whether the drag and drop is enabled or not on the webview. By default it is enabled.
*
* Disabling it is required to use drag and drop on the frontend on Windows.
* Disabling it is required to use HTML5 drag and drop on the frontend on Windows.
*/
fileDropEnabled?: boolean
dragDropEnabled?: boolean
/**
* Whether clicking an inactive webview also clicks through to the webview on macOS.
*/
@ -633,4 +652,4 @@ interface WebviewOptions {
export { Webview, getCurrent, getAll }
export type { FileDropEvent, FileDropPayload, WebviewOptions }
export type { DragDropEvent, DragDropPayload, WebviewOptions }

View File

@ -13,7 +13,7 @@ import { Window } from './window'
import { listen, once } from './event'
import type { EventName, EventCallback, UnlistenFn } from './event'
import { invoke } from './core'
import type { FileDropEvent, FileDropPayload } from './webview'
import type { DragDropEvent, DragDropPayload } from './webview'
/**
* Get an instance of `Webview` for the current webview window.
@ -231,4 +231,4 @@ function applyMixins(
}
export { WebviewWindow, getCurrent, getAll }
export type { FileDropEvent, FileDropPayload }
export type { DragDropEvent, DragDropPayload }

View File

@ -35,7 +35,7 @@ import {
} from './event'
import { invoke } from './core'
import { WebviewWindow } from './webviewWindow'
import type { FileDropEvent, FileDropPayload } from './webview'
import type { DragDropEvent, DragDropPayload } from './webview'
import { Image, transformImage } from './image'
/**
@ -1717,7 +1717,7 @@ class Window {
* @example
* ```typescript
* import { getCurrent } from "@tauri-apps/api/webview";
* const unlisten = await getCurrent().onFileDropEvent((event) => {
* const unlisten = await getCurrent().onDragDropEvent((event) => {
* if (event.payload.type === 'hover') {
* console.log('User hovering', event.payload.paths);
* } else if (event.payload.type === 'drop') {
@ -1734,16 +1734,16 @@ class Window {
* @returns A promise resolving to a function to unlisten to the event.
* Note that removing the listener is required if your listener goes out of scope e.g. the component is unmounted.
*/
async onFileDropEvent(
handler: EventCallback<FileDropEvent>
async onDragDropEvent(
handler: EventCallback<DragDropEvent>
): Promise<UnlistenFn> {
const unlistenFileDrop = await this.listen<FileDropPayload>(
TauriEvent.FILE_DROP,
const unlistenDrag = await this.listen<DragDropPayload>(
TauriEvent.DRAG,
(event) => {
handler({
...event,
payload: {
type: 'drop',
type: 'dragged',
paths: event.payload.paths,
position: mapPhysicalPosition(event.payload.position)
}
@ -1751,13 +1751,13 @@ class Window {
}
)
const unlistenFileHover = await this.listen<FileDropPayload>(
TauriEvent.FILE_DROP_HOVER,
const unlistenDrop = await this.listen<DragDropPayload>(
TauriEvent.DROP,
(event) => {
handler({
...event,
payload: {
type: 'hover',
type: 'dropped',
paths: event.payload.paths,
position: mapPhysicalPosition(event.payload.position)
}
@ -1765,16 +1765,30 @@ class Window {
}
)
const unlistenDragOver = await this.listen<DragDropPayload>(
TauriEvent.DROP_OVER,
(event) => {
handler({
...event,
payload: {
type: 'dragOver',
position: mapPhysicalPosition(event.payload.position)
}
})
}
)
const unlistenCancel = await this.listen<null>(
TauriEvent.FILE_DROP_CANCELLED,
TauriEvent.DROP_CANCELLED,
(event) => {
handler({ ...event, payload: { type: 'cancel' } })
handler({ ...event, payload: { type: 'cancelled' } })
}
)
return () => {
unlistenFileDrop()
unlistenFileHover()
unlistenDrag()
unlistenDrop()
unlistenDragOver()
unlistenCancel()
}
}
@ -2264,6 +2278,6 @@ export type {
ScaleFactorChanged,
WindowOptions,
Color,
FileDropEvent,
FileDropPayload
DragDropEvent,
DragDropPayload
}

View File

@ -221,8 +221,8 @@
"null"
]
},
"fileDropEnabled": {
"description": "Whether the file drop is enabled or not on the webview. By default it is enabled.\n\nDisabling it is required to use drag and drop on the frontend on Windows.",
"dragDropEnabled": {
"description": "Whether the drag and drop is enabled or not on the webview. By default it is enabled.\n\nDisabling it is required to use HTML5 drag and drop on the frontend on Windows.",
"default": true,
"type": "boolean"
},