1
1
mirror of https://github.com/wez/wezterm.git synced 2025-01-08 23:17:36 +03:00

Add support for drag and drop files in Windows (#1953)

* Add support for drag and drop files in Windows

* Add two drag and drop filename quoting patterns (mainly) for Windows, change doc examples.

* Code style cleanup

* Improve Windows quoting pattern and rename DoubleQuoteAlways to WindowsAlwaysQuoted

* Improve special char finding for DroppedFileQuoting::Windows and fix doc.
This commit is contained in:
datasone 2022-05-09 22:38:52 +08:00 committed by GitHub
parent 7858f652fb
commit 74e1cdcb40
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 67 additions and 9 deletions

View File

@ -1476,11 +1476,19 @@ pub enum DroppedFileQuoting {
SpacesOnly,
/// Use POSIX style shell word escaping
Posix,
/// Use Windows style shell word escaping
Windows,
/// Always double quote the file name
WindowsAlwaysQuoted,
}
impl Default for DroppedFileQuoting {
fn default() -> Self {
Self::SpacesOnly
if cfg!(windows) {
Self::Windows
} else {
Self::SpacesOnly
}
}
}
@ -1491,6 +1499,15 @@ impl DroppedFileQuoting {
Self::SpacesOnly => s.replace(" ", "\\ "),
// https://docs.rs/shlex/latest/shlex/fn.quote.html
Self::Posix => shlex::quote(s).into_owned().to_string(),
Self::Windows => {
let chars_need_quoting = [' ', '\t', '\n', '\x0b', '\"'];
if s.chars().any(|c| chars_need_quoting.contains(&c)) {
format!("\"{}\"", s)
} else {
s.to_string()
}
}
Self::WindowsAlwaysQuoted => format!("\"{}\"", s),
}
}
}

View File

@ -3,25 +3,29 @@
*Since: nightly builds only*
Controls how file names are quoted (or not) when dragging and dropping.
There are three possible values:
There are five possible values:
* `"None"` - no quoting is performed, the file name is passed through as-is.
* `"SpacesOnly"` - backslash-escape only spaces, leaving all other characters as-is. This is the default.
* `"SpacesOnly"` - backslash-escape only spaces, leaving all other characters as-is. This is the default for non-Windows platforms.
* `"Posix"` - use POSIX style shell word escaping.
* `"Windows"` - use Windows style shell word escaping: double-quote filename with space characters in it, and leaving others as-is. This is the default on Windows.
* `"WindowsAlwaysQuoted"` - like `"Windows"`, while always double-quote the filename.
For example:
|`quote_dropped_files` |file name |quoted result |
|----------------------|-------------|---------------|
|`"None"` |`hello world`|`hello world` |
|`"SpacesOnly"` |`hello world`|`hello\ world` |
|`"Posix"` |`hello world`|`"hello world"`|
| `quote_dropped_files` | file name | quoted result |
|-------------------------|------------------|---------------------|
| `"None"` | `hello ($world)` | `hello ($world)` |
| `"SpacesOnly"` | `hello ($world)` | `hello\ ($world)` |
| `"Posix"` | `hello ($world)` | `"hello (\$world)"` |
| `"Windows"` | `hello ($world)` | `"hello ($world)"` |
| `"WindowsAlwaysQuoted"` | `hello ($world)` | `"hello ($world)"` |
Drag and drop support for files is a platform dependent feature
|Platform |Supported since |
|----------|-------------------|
|macOS |nightly builds only|
|Windows |Not yet |
|Windows |nightly builds only|
|X11 |Not yet |
|Wayland |Not yet |

View File

@ -22,6 +22,7 @@ use std::convert::TryInto;
use std::ffi::OsString;
use std::io::{self, Error as IoError};
use std::os::windows::ffi::OsStringExt;
use std::path::PathBuf;
use std::ptr::{null, null_mut};
use std::rc::Rc;
use std::sync::Mutex;
@ -33,6 +34,7 @@ use winapi::shared::windef::*;
use winapi::shared::winerror::S_OK;
use winapi::um::imm::*;
use winapi::um::libloaderapi::GetModuleHandleW;
use winapi::um::shellapi::{DragAcceptFiles, DragFinish, DragQueryFileW, HDROP};
use winapi::um::sysinfoapi::{GetTickCount, GetVersionExW};
use winapi::um::uxtheme::{
CloseThemeData, GetThemeFont, GetThemeSysFont, OpenThemeData, SetWindowTheme,
@ -637,6 +639,11 @@ impl Window {
apply_theme(hwnd.0);
enable_blur_behind(hwnd.0);
// Make window capable of accepting drag and drop
unsafe {
DragAcceptFiles(hwnd.0, winapi::shared::minwindef::TRUE);
}
Connection::get()
.expect("Connection::init was not called")
.windows
@ -2406,6 +2413,35 @@ unsafe fn key(hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM) -> Option<L
None
}
unsafe fn drop_files(hwnd: HWND, _msg: UINT, wparam: WPARAM, _lparam: LPARAM) -> Option<LRESULT> {
let inner = rc_from_hwnd(hwnd)?;
let h_drop = wparam as HDROP;
// Get the number of files dropped
let file_count = DragQueryFileW(h_drop, 0xFFFFFFFF, null_mut(), 0);
let mut filenames: Vec<PathBuf> = Vec::with_capacity(file_count as usize);
for idx in 0..file_count {
// The returned size of buffer is in characters, not including the terminating null character
let buf_size = DragQueryFileW(h_drop, idx, null_mut(), 0);
if buf_size > 0 {
// Windows will truncate the filename and add null terminator if space isn't enough
let buf_size = buf_size as usize + 1;
let mut wide_buf = vec![0u16; buf_size];
DragQueryFileW(h_drop, idx, wide_buf.as_mut_ptr(), wide_buf.len() as u32);
wide_buf.pop(); // Drops the null terminator
filenames.push(OsString::from_wide(&wide_buf).into());
}
}
let mut inner = inner.borrow_mut();
inner.events.dispatch(WindowEvent::DroppedFile(filenames));
DragFinish(h_drop);
Some(0)
}
unsafe fn do_wnd_proc(hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM) -> Option<LRESULT> {
match msg {
WM_NCCREATE => wm_nccreate(hwnd, msg, wparam, lparam),
@ -2434,6 +2470,7 @@ unsafe fn do_wnd_proc(hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM) ->
| WM_RBUTTONDOWN | WM_RBUTTONUP | WM_MBUTTONDOWN | WM_MBUTTONUP => {
mouse_button(hwnd, msg, wparam, lparam)
}
WM_DROPFILES => drop_files(hwnd, msg, wparam, lparam),
WM_ERASEBKGND => Some(1),
WM_CLOSE => {
if let Some(inner) = rc_from_hwnd(hwnd) {