Skip to content

Commit cd7291e

Browse files
committed
update
1 parent 751cb9e commit cd7291e

File tree

3 files changed

+312
-1
lines changed

3 files changed

+312
-1
lines changed

crates/rs-gui/try-wry/Cargo.toml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,16 @@ path = "examples/try01b.rs"
1919
name = "t02"
2020
path = "examples/try02.rs"
2121

22-
# multiwindow
22+
# streaming
2323
[[bin]]
2424
name = "t03"
2525
path = "examples/try03.rs"
2626

27+
# custom_titlebar
28+
[[bin]]
29+
name = "t04"
30+
path = "examples/try04.rs"
31+
2732

2833
[dependencies]
2934
getrandom = "0.3.1"
Lines changed: 299 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,299 @@
1+
// Copyright 2020-2023 Tauri Programme within The Commons Conservancy
2+
// SPDX-License-Identifier: Apache-2.0
3+
// SPDX-License-Identifier: MIT
4+
5+
use tao::{
6+
dpi::PhysicalSize,
7+
event::{Event, StartCause, WindowEvent},
8+
event_loop::{ControlFlow, EventLoopBuilder},
9+
window::{CursorIcon, ResizeDirection, Window, WindowBuilder},
10+
};
11+
use wry::{WebViewBuilder, http::Request};
12+
13+
#[derive(Debug)]
14+
enum HitTestResult {
15+
Client,
16+
Left,
17+
Right,
18+
Top,
19+
Bottom,
20+
TopLeft,
21+
TopRight,
22+
BottomLeft,
23+
BottomRight,
24+
NoWhere,
25+
}
26+
27+
impl HitTestResult {
28+
fn drag_resize_window(&self, window: &Window) {
29+
let _ = window.drag_resize_window(match self {
30+
HitTestResult::Left => ResizeDirection::West,
31+
HitTestResult::Right => ResizeDirection::East,
32+
HitTestResult::Top => ResizeDirection::North,
33+
HitTestResult::Bottom => ResizeDirection::South,
34+
HitTestResult::TopLeft => ResizeDirection::NorthWest,
35+
HitTestResult::TopRight => ResizeDirection::NorthEast,
36+
HitTestResult::BottomLeft => ResizeDirection::SouthWest,
37+
HitTestResult::BottomRight => ResizeDirection::SouthEast,
38+
_ => unreachable!(),
39+
});
40+
}
41+
42+
fn change_cursor(&self, window: &Window) {
43+
window.set_cursor_icon(match self {
44+
HitTestResult::Left => CursorIcon::WResize,
45+
HitTestResult::Right => CursorIcon::EResize,
46+
HitTestResult::Top => CursorIcon::NResize,
47+
HitTestResult::Bottom => CursorIcon::SResize,
48+
HitTestResult::TopLeft => CursorIcon::NwResize,
49+
HitTestResult::TopRight => CursorIcon::NeResize,
50+
HitTestResult::BottomLeft => CursorIcon::SwResize,
51+
HitTestResult::BottomRight => CursorIcon::SeResize,
52+
_ => CursorIcon::Default,
53+
});
54+
}
55+
}
56+
57+
fn hit_test(window_size: PhysicalSize<u32>, x: i32, y: i32, scale: f64) -> HitTestResult {
58+
const BORDERLESS_RESIZE_INSET: f64 = 5.0;
59+
60+
const CLIENT: isize = 0b0000;
61+
const LEFT: isize = 0b0001;
62+
const RIGHT: isize = 0b0010;
63+
const TOP: isize = 0b0100;
64+
const BOTTOM: isize = 0b1000;
65+
const TOPLEFT: isize = TOP | LEFT;
66+
const TOPRIGHT: isize = TOP | RIGHT;
67+
const BOTTOMLEFT: isize = BOTTOM | LEFT;
68+
const BOTTOMRIGHT: isize = BOTTOM | RIGHT;
69+
70+
let top = 0;
71+
let left = 0;
72+
let bottom = top + window_size.height as i32;
73+
let right = left + window_size.width as i32;
74+
75+
let inset = (BORDERLESS_RESIZE_INSET * scale) as i32;
76+
77+
#[rustfmt::skip]
78+
let result =
79+
(LEFT * (if x < (left + inset) { 1 } else { 0 }))
80+
| (RIGHT * (if x >= (right - inset) { 1 } else { 0 }))
81+
| (TOP * (if y < (top + inset) { 1 } else { 0 }))
82+
| (BOTTOM * (if y >= (bottom - inset) { 1 } else { 0 }));
83+
84+
match result {
85+
CLIENT => HitTestResult::Client,
86+
LEFT => HitTestResult::Left,
87+
RIGHT => HitTestResult::Right,
88+
TOP => HitTestResult::Top,
89+
BOTTOM => HitTestResult::Bottom,
90+
TOPLEFT => HitTestResult::TopLeft,
91+
TOPRIGHT => HitTestResult::TopRight,
92+
BOTTOMLEFT => HitTestResult::BottomLeft,
93+
BOTTOMRIGHT => HitTestResult::BottomRight,
94+
_ => HitTestResult::NoWhere,
95+
}
96+
}
97+
98+
enum UserEvent {
99+
Minimize,
100+
Maximize,
101+
DragWindow,
102+
CloseWindow,
103+
MouseDown(i32, i32),
104+
MouseMove(i32, i32),
105+
}
106+
107+
fn main() -> wry::Result<()> {
108+
let event_loop = EventLoopBuilder::<UserEvent>::with_user_event().build();
109+
let window = WindowBuilder::new().with_decorations(false).build(&event_loop).unwrap();
110+
111+
const HTML: &str = r#"
112+
<html>
113+
114+
<head>
115+
<style>
116+
html {
117+
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
118+
}
119+
120+
* {
121+
padding: 0;
122+
margin: 0;
123+
box-sizing: border-box;
124+
}
125+
126+
*[data-wry-darg-region] {
127+
app-region: drag; /* Supported on Windows only, on WebView2 123+ and makes dragging with touch work */
128+
}
129+
130+
main {
131+
display: grid;
132+
place-items: center;
133+
height: calc(100vh - 30px);
134+
}
135+
136+
.titlebar {
137+
height: 30px;
138+
padding-left: 5px;
139+
display: grid;
140+
grid-auto-flow: column;
141+
grid-template-columns: 1fr max-content max-content max-content;
142+
align-items: center;
143+
background: #1F1F1F;
144+
color: white;
145+
user-select: none;
146+
}
147+
148+
.titlebar-button {
149+
display: inline-flex;
150+
justify-content: center;
151+
align-items: center;
152+
width: 30px;
153+
height: 30px;
154+
}
155+
156+
.titlebar-button:hover {
157+
background: #3b3b3b;
158+
}
159+
160+
.titlebar-button#close:hover {
161+
background: #da3d3d;
162+
}
163+
164+
.titlebar-button img {
165+
filter: invert(100%);
166+
}
167+
</style>
168+
</head>
169+
170+
<body>
171+
<div class="titlebar">
172+
<div data-wry-darg-region>Custom Titlebar</div>
173+
<div>
174+
<div class="titlebar-button" onclick="window.ipc.postMessage('minimize')">
175+
<img src="https://api.iconify.design/codicon:chrome-minimize.svg" />
176+
</div>
177+
<div class="titlebar-button" onclick="window.ipc.postMessage('maximize')">
178+
<img src="https://api.iconify.design/codicon:chrome-maximize.svg" />
179+
</div>
180+
<div class="titlebar-button" id="close" onclick="window.ipc.postMessage('close')">
181+
<img src="https://api.iconify.design/codicon:close.svg" />
182+
</div>
183+
</div>
184+
</div>
185+
<main>
186+
<h4> WRYYYYYYYYYYYYYYYYYYYYYY! </h4>
187+
</main>
188+
<script>
189+
document.addEventListener('mousemove', (e) => window.ipc.postMessage(`mousemove:${e.clientX},${e.clientY}`))
190+
document.addEventListener('mousedown', (e) => {
191+
if (e.target.hasAttribute('data-wry-darg-region') && e.button === 0) {
192+
e.detail === 2
193+
? window.ipc.postMessage('maximize')
194+
: window.ipc.postMessage('drag_window');
195+
} else {
196+
window.ipc.postMessage(`mousedown:${e.clientX},${e.clientY}`);
197+
}
198+
})
199+
document.addEventListener('touchstart', (e) => {
200+
if (e.target.hasAttribute('data-wry-darg-region')) {
201+
window.ipc.postMessage('drag_window');
202+
}
203+
})
204+
</script>
205+
</body>
206+
207+
</html>
208+
"#;
209+
210+
let proxy = event_loop.create_proxy();
211+
let handler = move |req: Request<String>| {
212+
let body = req.body();
213+
let mut req = body.split([':', ',']);
214+
match req.next().unwrap() {
215+
"minimize" => {
216+
let _ = proxy.send_event(UserEvent::Minimize);
217+
},
218+
"maximize" => {
219+
let _ = proxy.send_event(UserEvent::Maximize);
220+
},
221+
"drag_window" => {
222+
let _ = proxy.send_event(UserEvent::DragWindow);
223+
},
224+
"close" => {
225+
let _ = proxy.send_event(UserEvent::CloseWindow);
226+
},
227+
"mousedown" => {
228+
let x = req.next().unwrap().parse().unwrap();
229+
let y = req.next().unwrap().parse().unwrap();
230+
let _ = proxy.send_event(UserEvent::MouseDown(x, y));
231+
},
232+
"mousemove" => {
233+
let x = req.next().unwrap().parse().unwrap();
234+
let y = req.next().unwrap().parse().unwrap();
235+
let _ = proxy.send_event(UserEvent::MouseMove(x, y));
236+
},
237+
_ => {},
238+
}
239+
};
240+
241+
let builder = WebViewBuilder::new()
242+
.with_html(HTML)
243+
.with_ipc_handler(handler)
244+
.with_accept_first_mouse(true);
245+
246+
#[cfg(any(
247+
target_os = "windows",
248+
target_os = "macos",
249+
target_os = "ios",
250+
target_os = "android"
251+
))]
252+
let webview = builder.build(&window)?;
253+
#[cfg(not(any(
254+
target_os = "windows",
255+
target_os = "macos",
256+
target_os = "ios",
257+
target_os = "android"
258+
)))]
259+
let webview = {
260+
use tao::platform::unix::WindowExtUnix;
261+
use wry::WebViewBuilderExtUnix;
262+
let vbox = window.default_vbox().unwrap();
263+
builder.build_gtk(vbox)?
264+
};
265+
266+
let mut webview = Some(webview);
267+
268+
event_loop.run(move |event, _, control_flow| {
269+
*control_flow = ControlFlow::Wait;
270+
271+
match event {
272+
Event::NewEvents(StartCause::Init) => println!("Wry application started!"),
273+
Event::WindowEvent { event: WindowEvent::CloseRequested, .. }
274+
| Event::UserEvent(UserEvent::CloseWindow) => {
275+
let _ = webview.take();
276+
*control_flow = ControlFlow::Exit
277+
},
278+
279+
Event::UserEvent(e) => match e {
280+
UserEvent::Minimize => window.set_minimized(true),
281+
UserEvent::Maximize => window.set_maximized(!window.is_maximized()),
282+
UserEvent::DragWindow => window.drag_window().unwrap(),
283+
UserEvent::MouseDown(x, y) => {
284+
let res = hit_test(window.inner_size(), x, y, window.scale_factor());
285+
match res {
286+
HitTestResult::Client | HitTestResult::NoWhere => {},
287+
_ => res.drag_resize_window(&window),
288+
}
289+
},
290+
UserEvent::MouseMove(x, y) => {
291+
hit_test(window.inner_size(), x, y, window.scale_factor())
292+
.change_cursor(&window);
293+
},
294+
UserEvent::CloseWindow => { /* handled above */ },
295+
},
296+
_ => (),
297+
}
298+
});
299+
}

crates/rs-gui/try-wry/readme.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@ task gui:wry:r -- --bin t01b
2424

2525
# multiwindow
2626
task gui:wry:r -- --bin t02
27+
28+
# streaming
29+
task gui:wry:r -- --bin t03
30+
31+
# custom_titlebar, 自定义窗口标题栏 & 窗口关闭按扭
32+
task gui:wry:r -- --bin t04
33+
2734
```
2835

2936

0 commit comments

Comments
 (0)