Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(Windows): resize borderless window #333

Merged
merged 10 commits into from
Jul 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changes/borderless-resize.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"wry": patch
---

Allow resizing of borderless window on Windows
4 changes: 3 additions & 1 deletion examples/custom_titlebar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ fn main() -> wry::Result<()> {

document.addEventListener('mousedown', (e) => {
if (e.target.classList.contains('drag-region') && e.buttons === 1) {
window.rpc.notify('drag_window');
e.detail === 2
? window.rpc.notify('maximize')
: window.rpc.notify('drag_window');
}
})

Expand Down
25 changes: 16 additions & 9 deletions src/webview/webkitgtk/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use std::rc::Rc;

use gdk::{WindowEdge, WindowExt, RGBA};
use gdk::{WindowEdge, RGBA};
use gio::Cancellable;
use glib::{signal::Inhibit, Bytes, Cast, FileError};
use gtk::{BoxExt, ContainerExt, GtkWindowExt, WidgetExt};
Expand Down Expand Up @@ -101,26 +101,33 @@ impl InnerWebView {
webview.connect_button_press_event(|webview, event| {
if event.get_button() == 1 {
let (cx, cy) = event.get_root();
if let Some(window) = webview.get_parent_window() {
let result = crate::application::platform::unix::hit_test(&window, cx, cy);
// This one should be GtkBox
if let Some(widget) = webview.get_parent() {
// This one should be GtkWindow
if let Some(window) = widget.get_parent() {
// Safe to unwrap unless this is not from tao
let window: gtk::Window = window.downcast().unwrap();
if !window.get_decorated() && window.get_resizable() {
// Safe to unwrap since it's a valide GtkWindow
let result = hit_test(&window.get_window().unwrap(), cx, cy);

// this check is necessary, otherwise the webview won't recieve the click properly when resize isn't needed
if result != WindowEdge::__Unknown(8) {
window.begin_resize_drag(result, 1, cx as i32, cy as i32, event.get_time());
// this check is necessary, otherwise the webview won't recieve the click properly when resize isn't needed
if result != WindowEdge::__Unknown(8) {
window.begin_resize_drag(result, 1, cx as i32, cy as i32, event.get_time());
}
}
}
}
}
Inhibit(false)
});

// Gtk application window can only contain one widget at a time.
// In tao, we add a gtk box if menu bar is required. So we check if
// In tao, we add a GtkBox to pack menu bar. So we check if
// there's a box widget here.
if let Some(widget) = window.get_children().pop() {
let vbox = widget.downcast::<gtk::Box>().unwrap();
vbox.pack_start(&*webview, true, true, 0);
} else {
window.add(&*webview);
}
webview.grab_focus();

Expand Down
55 changes: 50 additions & 5 deletions src/webview/webview2/win32/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,14 @@ impl InnerWebView {

// Initialize scripts
w.add_script_to_execute_on_document_created(
"window.external={invoke:s=>window.chrome.webview.postMessage(s)}",
r#"
window.external={invoke:s=>window.chrome.webview.postMessage(s)};

window.addEventListener('mousedown', (e) => {
if (e.buttons === 1) window.chrome.webview.postMessage('__WEBVIEW_LEFT_MOUSE_DOWN__')
});
window.addEventListener('mousemove', () => window.chrome.webview.postMessage('__WEBVIEW_MOUSE_MOVE__'));
"#,
|_| (Ok(())),
)?;
for js in attributes.initialization_scripts {
Expand All @@ -116,6 +123,46 @@ impl InnerWebView {
let rpc_handler = attributes.rpc_handler.take();
w.add_web_message_received(move |webview, args| {
let js = args.try_get_web_message_as_string()?;
if js == "__WEBVIEW_LEFT_MOUSE_DOWN__" || js == "__WEBVIEW_MOUSE_MOVE__" {
if !window_.is_decorated() && window_.is_resizable() {
use winapi::um::winuser::{
HTBOTTOM, HTBOTTOMLEFT, HTBOTTOMRIGHT, HTLEFT, HTRIGHT,
HTTOP, HTTOPLEFT, HTTOPRIGHT, HTCLIENT, GetCursorPos,
};
use crate::application::{window::CursorIcon,platform::windows::hit_test};

let (cx, cy);
unsafe {
let mut point = std::mem::zeroed();
GetCursorPos(&mut point);
cx = point.x;
cy = point.y;
};
let result = hit_test(window_.hwnd() as _, cx, cy);
let cursor = match result {
HTLEFT => CursorIcon::WResize,
HTTOP => CursorIcon::NResize,
HTRIGHT => CursorIcon::EResize,
HTBOTTOM => CursorIcon::SResize,
HTTOPLEFT => CursorIcon::NwResize,
HTTOPRIGHT => CursorIcon::NeResize,
HTBOTTOMLEFT => CursorIcon::SwResize,
HTBOTTOMRIGHT => CursorIcon::SeResize,
_ => CursorIcon::Arrow,
};
window_.set_cursor_icon(cursor);

if js == "__WEBVIEW_LEFT_MOUSE_DOWN__" {
// this check is necessary, otherwise any window dragging implementation won't work
if result != HTCLIENT {
window_.begin_resize_drag(result);
}
}
}
// these are internal messages, rpc_handlers don't need it so exit early
return Ok(());
}

if let Some(rpc_handler) = &rpc_handler {
match super::rpc_proxy(&window_, js, rpc_handler) {
Ok(result) => {
Expand Down Expand Up @@ -240,6 +287,7 @@ impl InnerWebView {
}
}
});
// TODO: OnceCell into_inner for controller

Ok(Self {
controller,
Expand All @@ -263,17 +311,14 @@ impl InnerWebView {
let hwnd = hwnd as HWND;

// Safety: System calls are unsafe
// XXX: Resizing on Windows is usually sluggish. Many other applications share same behavior.
unsafe {
let mut rect = std::mem::zeroed();
GetClientRect(hwnd, &mut rect);
if let Some(c) = self.controller.get() {
rect.left = rect.left + 1;
c.put_bounds(rect)?;
rect.left = rect.left - 1;
c.put_bounds(rect)?;
}
}

Ok(())
}

Expand Down