mirror of
https://github.com/mileswolfallen2/gelectron.git
synced 2026-09-08 12:43:16 +00:00
305 lines
9.0 KiB
Rust
305 lines
9.0 KiB
Rust
// Copyright 2020-2023 Tauri Programme within The Commons Conservancy
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
use tao::{
|
|
dpi::PhysicalSize,
|
|
event::{Event, StartCause, WindowEvent},
|
|
event_loop::{ControlFlow, EventLoopBuilder},
|
|
window::{CursorIcon, ResizeDirection, Window, WindowBuilder},
|
|
};
|
|
use wry::{http::Request, WebViewBuilder};
|
|
|
|
#[derive(Debug)]
|
|
enum HitTestResult {
|
|
Client,
|
|
Left,
|
|
Right,
|
|
Top,
|
|
Bottom,
|
|
TopLeft,
|
|
TopRight,
|
|
BottomLeft,
|
|
BottomRight,
|
|
NoWhere,
|
|
}
|
|
|
|
impl HitTestResult {
|
|
fn drag_resize_window(&self, window: &Window) {
|
|
let _ = window.drag_resize_window(match self {
|
|
HitTestResult::Left => ResizeDirection::West,
|
|
HitTestResult::Right => ResizeDirection::East,
|
|
HitTestResult::Top => ResizeDirection::North,
|
|
HitTestResult::Bottom => ResizeDirection::South,
|
|
HitTestResult::TopLeft => ResizeDirection::NorthWest,
|
|
HitTestResult::TopRight => ResizeDirection::NorthEast,
|
|
HitTestResult::BottomLeft => ResizeDirection::SouthWest,
|
|
HitTestResult::BottomRight => ResizeDirection::SouthEast,
|
|
_ => unreachable!(),
|
|
});
|
|
}
|
|
|
|
fn change_cursor(&self, window: &Window) {
|
|
window.set_cursor_icon(match self {
|
|
HitTestResult::Left => CursorIcon::WResize,
|
|
HitTestResult::Right => CursorIcon::EResize,
|
|
HitTestResult::Top => CursorIcon::NResize,
|
|
HitTestResult::Bottom => CursorIcon::SResize,
|
|
HitTestResult::TopLeft => CursorIcon::NwResize,
|
|
HitTestResult::TopRight => CursorIcon::NeResize,
|
|
HitTestResult::BottomLeft => CursorIcon::SwResize,
|
|
HitTestResult::BottomRight => CursorIcon::SeResize,
|
|
_ => CursorIcon::Default,
|
|
});
|
|
}
|
|
}
|
|
|
|
fn hit_test(window_size: PhysicalSize<u32>, x: i32, y: i32, scale: f64) -> HitTestResult {
|
|
const BORDERLESS_RESIZE_INSET: f64 = 5.0;
|
|
|
|
const CLIENT: isize = 0b0000;
|
|
const LEFT: isize = 0b0001;
|
|
const RIGHT: isize = 0b0010;
|
|
const TOP: isize = 0b0100;
|
|
const BOTTOM: isize = 0b1000;
|
|
const TOPLEFT: isize = TOP | LEFT;
|
|
const TOPRIGHT: isize = TOP | RIGHT;
|
|
const BOTTOMLEFT: isize = BOTTOM | LEFT;
|
|
const BOTTOMRIGHT: isize = BOTTOM | RIGHT;
|
|
|
|
let top = 0;
|
|
let left = 0;
|
|
let bottom = top + window_size.height as i32;
|
|
let right = left + window_size.width as i32;
|
|
|
|
let inset = (BORDERLESS_RESIZE_INSET * scale) as i32;
|
|
|
|
#[rustfmt::skip]
|
|
let result =
|
|
(LEFT * (if x < (left + inset) { 1 } else { 0 }))
|
|
| (RIGHT * (if x >= (right - inset) { 1 } else { 0 }))
|
|
| (TOP * (if y < (top + inset) { 1 } else { 0 }))
|
|
| (BOTTOM * (if y >= (bottom - inset) { 1 } else { 0 }));
|
|
|
|
match result {
|
|
CLIENT => HitTestResult::Client,
|
|
LEFT => HitTestResult::Left,
|
|
RIGHT => HitTestResult::Right,
|
|
TOP => HitTestResult::Top,
|
|
BOTTOM => HitTestResult::Bottom,
|
|
TOPLEFT => HitTestResult::TopLeft,
|
|
TOPRIGHT => HitTestResult::TopRight,
|
|
BOTTOMLEFT => HitTestResult::BottomLeft,
|
|
BOTTOMRIGHT => HitTestResult::BottomRight,
|
|
_ => HitTestResult::NoWhere,
|
|
}
|
|
}
|
|
|
|
enum UserEvent {
|
|
Minimize,
|
|
Maximize,
|
|
DragWindow,
|
|
CloseWindow,
|
|
MouseDown(i32, i32),
|
|
MouseMove(i32, i32),
|
|
}
|
|
|
|
fn main() -> wry::Result<()> {
|
|
let event_loop = EventLoopBuilder::<UserEvent>::with_user_event().build();
|
|
let window = WindowBuilder::new()
|
|
.with_decorations(false)
|
|
.build(&event_loop)
|
|
.unwrap();
|
|
|
|
const HTML: &str = r#"
|
|
<html>
|
|
|
|
<head>
|
|
<style>
|
|
html {
|
|
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
|
|
}
|
|
|
|
* {
|
|
padding: 0;
|
|
margin: 0;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
*[data-wry-darg-region] {
|
|
app-region: drag; /* Supported on Windows only, on WebView2 123+ and makes dragging with touch work */
|
|
}
|
|
|
|
main {
|
|
display: grid;
|
|
place-items: center;
|
|
height: calc(100vh - 30px);
|
|
}
|
|
|
|
.titlebar {
|
|
height: 30px;
|
|
padding-left: 5px;
|
|
display: grid;
|
|
grid-auto-flow: column;
|
|
grid-template-columns: 1fr max-content max-content max-content;
|
|
align-items: center;
|
|
background: #1F1F1F;
|
|
color: white;
|
|
user-select: none;
|
|
}
|
|
|
|
.titlebar-button {
|
|
display: inline-flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
width: 30px;
|
|
height: 30px;
|
|
}
|
|
|
|
.titlebar-button:hover {
|
|
background: #3b3b3b;
|
|
}
|
|
|
|
.titlebar-button#close:hover {
|
|
background: #da3d3d;
|
|
}
|
|
|
|
.titlebar-button img {
|
|
filter: invert(100%);
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body>
|
|
<div class="titlebar">
|
|
<div data-wry-darg-region>Custom Titlebar</div>
|
|
<div>
|
|
<div class="titlebar-button" onclick="window.ipc.postMessage('minimize')">
|
|
<img src="https://api.iconify.design/codicon:chrome-minimize.svg" />
|
|
</div>
|
|
<div class="titlebar-button" onclick="window.ipc.postMessage('maximize')">
|
|
<img src="https://api.iconify.design/codicon:chrome-maximize.svg" />
|
|
</div>
|
|
<div class="titlebar-button" id="close" onclick="window.ipc.postMessage('close')">
|
|
<img src="https://api.iconify.design/codicon:close.svg" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<main>
|
|
<h4> WRYYYYYYYYYYYYYYYYYYYYYY! </h4>
|
|
</main>
|
|
<script>
|
|
document.addEventListener('mousemove', (e) => window.ipc.postMessage(`mousemove:${e.clientX},${e.clientY}`))
|
|
document.addEventListener('mousedown', (e) => {
|
|
if (e.target.hasAttribute('data-wry-darg-region') && e.button === 0) {
|
|
e.detail === 2
|
|
? window.ipc.postMessage('maximize')
|
|
: window.ipc.postMessage('drag_window');
|
|
} else {
|
|
window.ipc.postMessage(`mousedown:${e.clientX},${e.clientY}`);
|
|
}
|
|
})
|
|
document.addEventListener('touchstart', (e) => {
|
|
if (e.target.hasAttribute('data-wry-darg-region')) {
|
|
window.ipc.postMessage('drag_window');
|
|
}
|
|
})
|
|
</script>
|
|
</body>
|
|
|
|
</html>
|
|
"#;
|
|
|
|
let proxy = event_loop.create_proxy();
|
|
let handler = move |req: Request<String>| {
|
|
let body = req.body();
|
|
let mut req = body.split([':', ',']);
|
|
match req.next().unwrap() {
|
|
"minimize" => {
|
|
let _ = proxy.send_event(UserEvent::Minimize);
|
|
}
|
|
"maximize" => {
|
|
let _ = proxy.send_event(UserEvent::Maximize);
|
|
}
|
|
"drag_window" => {
|
|
let _ = proxy.send_event(UserEvent::DragWindow);
|
|
}
|
|
"close" => {
|
|
let _ = proxy.send_event(UserEvent::CloseWindow);
|
|
}
|
|
"mousedown" => {
|
|
let x = req.next().unwrap().parse().unwrap();
|
|
let y = req.next().unwrap().parse().unwrap();
|
|
let _ = proxy.send_event(UserEvent::MouseDown(x, y));
|
|
}
|
|
"mousemove" => {
|
|
let x = req.next().unwrap().parse().unwrap();
|
|
let y = req.next().unwrap().parse().unwrap();
|
|
let _ = proxy.send_event(UserEvent::MouseMove(x, y));
|
|
}
|
|
_ => {}
|
|
}
|
|
};
|
|
|
|
let builder = WebViewBuilder::new()
|
|
.with_html(HTML)
|
|
.with_ipc_handler(handler)
|
|
.with_accept_first_mouse(true);
|
|
|
|
#[cfg(any(
|
|
target_os = "windows",
|
|
target_os = "macos",
|
|
target_os = "ios",
|
|
target_os = "android"
|
|
))]
|
|
let webview = builder.build(&window)?;
|
|
#[cfg(not(any(
|
|
target_os = "windows",
|
|
target_os = "macos",
|
|
target_os = "ios",
|
|
target_os = "android"
|
|
)))]
|
|
let webview = {
|
|
use tao::platform::unix::WindowExtUnix;
|
|
use wry::WebViewBuilderExtUnix;
|
|
let vbox = window.default_vbox().unwrap();
|
|
builder.build_gtk(vbox)?
|
|
};
|
|
|
|
let mut webview = Some(webview);
|
|
|
|
event_loop.run(move |event, _, control_flow| {
|
|
*control_flow = ControlFlow::Wait;
|
|
|
|
match event {
|
|
Event::NewEvents(StartCause::Init) => println!("Wry application started!"),
|
|
Event::WindowEvent {
|
|
event: WindowEvent::CloseRequested,
|
|
..
|
|
}
|
|
| Event::UserEvent(UserEvent::CloseWindow) => {
|
|
let _ = webview.take();
|
|
*control_flow = ControlFlow::Exit
|
|
}
|
|
|
|
Event::UserEvent(e) => match e {
|
|
UserEvent::Minimize => window.set_minimized(true),
|
|
UserEvent::Maximize => window.set_maximized(!window.is_maximized()),
|
|
UserEvent::DragWindow => window.drag_window().unwrap(),
|
|
UserEvent::MouseDown(x, y) => {
|
|
let res = hit_test(window.inner_size(), x, y, window.scale_factor());
|
|
match res {
|
|
HitTestResult::Client | HitTestResult::NoWhere => {}
|
|
_ => res.drag_resize_window(&window),
|
|
}
|
|
}
|
|
UserEvent::MouseMove(x, y) => {
|
|
hit_test(window.inner_size(), x, y, window.scale_factor()).change_cursor(&window);
|
|
}
|
|
UserEvent::CloseWindow => { /* handled above */ }
|
|
},
|
|
_ => (),
|
|
}
|
|
});
|
|
}
|