1use ponsic_types::Recti;
2use std::cell::RefCell;
3use std::fmt::Debug;
4use std::ptr::{null, null_mut};
5use std::time::Duration;
6use winapi::shared::windef::*;
7use winapi::um::libloaderapi::GetModuleHandleW;
8use winapi::um::winuser::*;
9
10use crate::{SystemError, check_error, make_ptr};
11
12use super::timer::Timer;
13
14#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
17pub enum WindowStyle {
18 AcceptFiles,
19 AppWindow,
20 ClientEdge,
21 Composited,
22 ContextHelp,
23 ControlParent,
24 DlgModalFrame,
25 Layered,
26 LayoutRtl,
27 Left,
28 LeftScrollBar,
29 LtrReading,
30 MdiChild,
31 NoActivate,
32 NoInheritLayout,
33 NoParentNotify,
34 NoRedirectionBitmap,
35 ExOverlappedWindow,
36 WindowEdge,
37 PaletteWindow,
38 ToolWindow,
39 TopMost,
40 Right,
41 RightScrollBar,
42 RtlReading,
43 StaticEdge,
44 Transparent,
45
46 ChildWindow,
47 ClipChildren,
48 ClipSiblings,
49 Disabled,
50 DlgFrame,
51 Group,
52 HScroll,
53 Maximize,
54 Minimize,
55 Iconic,
56 Child,
57 Popup,
58 Border,
59 PopupWindow,
60 TabStop,
61 SizeBox,
62 Tiled,
63 TiledWindow,
64 Overlapped,
65 Caption,
66 SysMenu,
67 ThickFrame,
68 MinimizeBox,
69 MaximizeBox,
70 OverlappedWindow,
71 Visible,
72 VScroll,
73}
74
75#[derive(Debug, Clone, Eq, PartialEq)]
77pub struct Window {
78 handle: HWND,
79}
80
81#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
85pub struct WindowHandle {
86 pub(crate) handle: HWND,
87}
88
89#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
90pub struct WindowId {
91 handle: usize,
92}
93
94impl Window {
95 fn new(handle: HWND) -> Self {
96 Self { handle }
97 }
98
99 pub fn handle(&self) -> WindowHandle {
100 WindowHandle {
101 handle: self.handle,
102 }
103 }
104}
105
106pub trait WindowManager {
107 fn get_handle(&self) -> usize;
108
109 fn id(&self) -> WindowId {
110 WindowId {
111 handle: self.get_handle() as _,
112 }
113 }
114
115 fn show(&self) {
117 let handle = self.get_handle() as HWND;
118 unsafe {
119 ShowWindow(handle, SW_SHOW);
120 UpdateWindow(handle);
121 }
122 }
123
124 fn hide(&self) {
126 let handle = self.get_handle() as HWND;
127 unsafe {
128 ShowWindow(handle, SW_HIDE);
129 }
130 }
131
132 fn set_text(&self, title: &str) {
137 let handle = self.get_handle() as HWND;
138 let title: Vec<u16> = String::from(title).encode_utf16().chain(Some(0)).collect();
139 unsafe {
140 SetWindowTextW(handle, title.as_ptr());
141 }
142 }
143
144 #[deprecated(
149 since = "1.0.0",
150 note = "此方法已弃用,请使用 WindowManager::set_text() 代替"
151 )]
152 fn set_title(&self, title: &str) {
153 let handle = self.get_handle() as HWND;
154 let title: Vec<u16> = String::from(title).encode_utf16().chain(Some(0)).collect();
155 unsafe {
156 SetWindowTextW(handle, title.as_ptr());
157 }
158 }
159
160 fn text(&self) -> String {
165 let handle = self.get_handle() as HWND;
166 unsafe {
167 let len = GetWindowTextLengthW(handle) as usize + 1;
168 let mut title: Vec<u16> = vec![0; len];
169 GetWindowTextW(handle, title.as_mut_ptr(), len as i32);
170 String::from_utf16(&title[0..len - 1]).unwrap()
171 }
172 }
173
174 #[deprecated(
179 since = "1.0.0",
180 note = "此方法已弃用,请使用 WindowManager::text() 代替"
181 )]
182 fn title(&self) -> String {
183 let handle = self.get_handle() as HWND;
184 unsafe {
185 let len = GetWindowTextLengthW(handle) as usize + 1;
186 let mut title: Vec<u16> = vec![0; len];
187 GetWindowTextW(handle, title.as_mut_ptr(), len as i32);
188 String::from_utf16(&title[0..len - 1]).unwrap()
189 }
190 }
191
192 fn set_parent(&self, parent: WindowId) {
194 let handle = self.get_handle() as HWND;
195 let parent = parent.handle as HWND;
196 unsafe {
197 SetParent(handle, parent);
198 }
199 }
200
201 fn parent(&self) -> Option<WindowHandle> {
203 let handle = self.get_handle() as HWND;
204 unsafe {
205 let parent = GetParent(handle);
206 if parent.is_null() {
207 None
208 } else {
209 Some(WindowHandle { handle })
210 }
211 }
212 }
213
214 fn redraw(&self) {
216 unsafe {
217 RedrawWindow(
218 self.get_handle() as _,
219 null_mut(),
220 null_mut(),
221 RDW_UPDATENOW | RDW_INVALIDATE,
222 );
223 }
224 }
225
226 fn get_rect(&self) -> Recti {
228 let mut rect = unsafe { std::mem::zeroed::<RECT>() };
229 unsafe { GetWindowRect(self.get_handle() as _, &mut rect) };
230 Recti::new(rect.left, rect.top, rect.right, rect.bottom)
231 }
232
233 fn get_client_rect(&self) -> Recti {
235 let mut rect = unsafe { std::mem::zeroed::<RECT>() };
236 unsafe { GetClientRect(self.get_handle() as _, &mut rect) };
237 Recti::new(rect.left, rect.top, rect.right, rect.bottom)
238 }
239
240 fn set_timer(&self, duration: Duration) -> super::Result<Timer> {
248 thread_local! {
249 static NEXT_TIMER_ID: RefCell<usize> = RefCell::new(0);
250 }
251 let uid = NEXT_TIMER_ID.with_borrow_mut(|f| {
252 *f += 1;
253 *f
254 });
255 unsafe {
256 if SetTimer(
257 self.get_handle() as _,
258 uid,
259 duration.as_millis().min(i32::MAX as _) as _,
260 None,
261 ) == 0
262 {
263 check_error()?;
264 }
265 };
266 Ok(Timer::new(self.get_handle() as _, uid))
267 }
268}
269
270impl Window {
271 pub unsafe fn send(window: WindowId, msg: u32, wparam: usize, lparam: isize) -> isize {
276 unsafe { SendMessageW(window.handle as _, msg, wparam, lparam) }
277 }
278
279 pub unsafe fn post(
284 window: WindowId,
285 msg: u32,
286 wparam: usize,
287 lparam: isize,
288 ) -> Result<i32, SystemError> {
289 match unsafe { PostMessageW(window.handle as _, msg, wparam, lparam) } {
290 0 => Err(check_error().unwrap_err()),
291 res @ _ => Ok(res),
292 }
293 }
294
295 pub const USER_DEF_BASE: u32 = WM_USER;
297 pub const APP_DEF_BASE: u32 = WM_APP;
298}
299
300impl WindowManager for Window {
301 fn get_handle(&self) -> usize {
302 self.handle as usize
303 }
304}
305
306impl WindowManager for WindowHandle {
307 fn get_handle(&self) -> usize {
308 self.handle as usize
309 }
310}
311
312#[derive(Debug)]
314pub struct Builder {
315 pos_size: (i32, i32, u32, u32),
316 class_name: String,
317 extra_styles: u32,
318 style: u32,
319 title: String,
320 parent: Option<WindowId>,
321 ptr: usize,
322}
323
324impl Builder {
325 pub(super) fn new(class_name: &str, pos_size: (i32, i32, u32, u32)) -> Self {
326 Self {
327 pos_size,
328 class_name: class_name.into(),
329 extra_styles: 0,
330 style: 0,
331 title: "Window".into(),
332 parent: None,
333 ptr: 0,
334 }
335 }
336
337 pub fn set_parent(mut self, window: WindowId) -> Self {
339 self.parent = Some(window);
340 self
341 }
342
343 pub fn set_style(mut self, style: &[WindowStyle]) -> Self {
347 style.iter().for_each(|style| match style {
348 WindowStyle::AcceptFiles => {
349 self.extra_styles |= WS_EX_ACCEPTFILES;
350 }
351 WindowStyle::AppWindow => {
352 self.extra_styles |= WS_EX_APPWINDOW;
353 }
354 WindowStyle::ClientEdge => {
355 self.extra_styles |= WS_EX_CLIENTEDGE;
356 }
357 WindowStyle::Composited => {
358 self.extra_styles |= WS_EX_COMPOSITED;
359 }
360 WindowStyle::Transparent => {
361 self.extra_styles |= WS_EX_TRANSPARENT;
362 }
363 WindowStyle::ContextHelp => {
364 self.extra_styles |= WS_EX_CONTEXTHELP;
365 }
366 WindowStyle::ControlParent => {
367 self.extra_styles |= WS_EX_CONTROLPARENT;
368 }
369 WindowStyle::DlgModalFrame => {
370 self.extra_styles |= WS_EX_DLGMODALFRAME;
371 }
372 WindowStyle::Layered => {
373 self.extra_styles |= WS_EX_LAYERED;
374 }
375 WindowStyle::LayoutRtl => {
376 self.extra_styles |= WS_EX_LAYOUTRTL;
377 }
378 WindowStyle::Left => {
379 self.extra_styles |= WS_EX_LEFT;
380 }
381 WindowStyle::LeftScrollBar => {
382 self.extra_styles |= WS_EX_LEFTSCROLLBAR;
383 }
384 WindowStyle::LtrReading => {
385 self.extra_styles |= WS_EX_LTRREADING;
386 }
387 WindowStyle::MdiChild => {
388 self.extra_styles |= WS_EX_MDICHILD;
389 }
390 WindowStyle::NoActivate => {
391 self.extra_styles |= WS_EX_NOACTIVATE;
392 }
393 WindowStyle::NoInheritLayout => {
394 self.extra_styles |= WS_EX_NOINHERITLAYOUT;
395 }
396 WindowStyle::NoParentNotify => {
397 self.extra_styles |= WS_EX_NOPARENTNOTIFY;
398 }
399 WindowStyle::NoRedirectionBitmap => {
400 self.extra_styles |= WS_EX_NOREDIRECTIONBITMAP;
401 }
402 WindowStyle::ExOverlappedWindow => {
403 self.extra_styles |= WS_EX_OVERLAPPEDWINDOW;
404 }
405 WindowStyle::WindowEdge => {
406 self.extra_styles |= WS_EX_WINDOWEDGE;
407 }
408 WindowStyle::PaletteWindow => {
409 self.extra_styles |= WS_EX_PALETTEWINDOW;
410 }
411 WindowStyle::ToolWindow => {
412 self.extra_styles |= WS_EX_TOOLWINDOW;
413 }
414 WindowStyle::TopMost => {
415 self.extra_styles |= WS_EX_TOPMOST;
416 }
417 WindowStyle::Right => {
418 self.extra_styles |= WS_EX_RIGHT;
419 }
420 WindowStyle::RightScrollBar => {
421 self.extra_styles |= WS_EX_RIGHTSCROLLBAR;
422 }
423 WindowStyle::RtlReading => {
424 self.extra_styles |= WS_EX_RTLREADING;
425 }
426 WindowStyle::StaticEdge => {
427 self.extra_styles |= WS_EX_STATICEDGE;
428 }
429 WindowStyle::ChildWindow => {
430 self.style |= WS_CHILDWINDOW;
431 }
432 WindowStyle::ClipChildren => {
433 self.style |= WS_CLIPCHILDREN;
434 }
435 WindowStyle::ClipSiblings => {
436 self.style |= WS_CLIPSIBLINGS;
437 }
438 WindowStyle::Disabled => {
439 self.style |= WS_DISABLED;
440 }
441 WindowStyle::DlgFrame => {
442 self.style |= WS_DLGFRAME;
443 }
444 WindowStyle::Group => {
445 self.style |= WS_GROUP;
446 }
447 WindowStyle::HScroll => {
448 self.style |= WS_HSCROLL;
449 }
450 WindowStyle::Maximize => {
451 self.style |= WS_MAXIMIZE;
452 }
453 WindowStyle::Minimize => {
454 self.style |= WS_MINIMIZE;
455 }
456 WindowStyle::Iconic => {
457 self.style |= WS_ICONIC;
458 }
459 WindowStyle::Child => {
460 self.style |= WS_CHILD;
461 }
462 WindowStyle::Popup => {
463 self.style |= WS_POPUP;
464 }
465 WindowStyle::Border => {
466 self.style |= WS_BORDER;
467 }
468 WindowStyle::PopupWindow => {
469 self.style |= WS_POPUPWINDOW;
470 }
471 WindowStyle::TabStop => {
472 self.style |= WS_TABSTOP;
473 }
474 WindowStyle::SizeBox => {
475 self.style |= WS_SIZEBOX;
476 }
477 WindowStyle::Tiled => {
478 self.style |= WS_TILED;
479 }
480 WindowStyle::TiledWindow => {
481 self.style |= WS_TILEDWINDOW;
482 }
483 WindowStyle::Overlapped => {
484 self.style |= WS_OVERLAPPED;
485 }
486 WindowStyle::Caption => {
487 self.style |= WS_CAPTION;
488 }
489 WindowStyle::SysMenu => {
490 self.style |= WS_SYSMENU;
491 }
492 WindowStyle::ThickFrame => {
493 self.style |= WS_THICKFRAME;
494 }
495 WindowStyle::MinimizeBox => {
496 self.style |= WS_MINIMIZEBOX;
497 }
498 WindowStyle::MaximizeBox => {
499 self.style |= WS_MAXIMIZEBOX;
500 }
501 WindowStyle::OverlappedWindow => {
502 self.style |= WS_OVERLAPPEDWINDOW;
503 }
504 WindowStyle::Visible => {
505 self.style |= WS_VISIBLE;
506 }
507 WindowStyle::VScroll => {
508 self.style |= WS_VSCROLL;
509 }
510 });
511 self
512 }
513
514 pub fn set_title(mut self, title: &str) -> Self {
516 self.title = title.into();
517 self
518 }
519
520 pub fn bind_data<T>(mut self, data: T) -> Self {
522 self.ptr = make_ptr(data) as _;
523 self
524 }
525
526 pub fn build(self) -> super::Result<Window> {
528 let class_name: Vec<u16> = self.class_name.encode_utf16().chain(Some(0)).collect();
529 let title: Vec<u16> = self.title.encode_utf16().chain(Some(0)).collect();
530 let handle = unsafe {
531 let handle = CreateWindowExW(
532 self.extra_styles,
533 class_name.as_ptr(),
534 title.as_ptr(),
535 self.style,
536 self.pos_size.0,
537 self.pos_size.1,
538 self.pos_size.2 as _,
539 self.pos_size.3 as _,
540 if let Some(window) = self.parent {
541 window.handle as HWND
542 } else {
543 null_mut()
544 },
545 null_mut(),
546 GetModuleHandleW(null()),
547 self.ptr as _,
548 );
549 if handle.is_null() {
550 check_error()?;
551 }
552 handle
553 };
554 Ok(Window::new(handle))
555 }
556}
557
558#[cfg(test)]
559mod tests {
560 use super::{super::Result, *};
561 use crate::win::class;
562 use ponsic_types::{Point, Recti as Rect, Size};
563
564 #[test]
565 fn window_builder_test() -> Result<()> {
566 let class = class::Registrar::new("window_builder_test").build()?;
567
568 let window = class
569 .make_window(Rect::from((Point::new(100, 100), Size::new(800, 600))))
570 .set_title("Test")
571 .set_style(&[WindowStyle::OverlappedWindow, WindowStyle::Border]);
572
573 #[allow(unused_variables)]
574 let window = window.build()?;
575
576 Ok(())
577 }
578}
579
580impl WindowHandle {
581 pub unsafe fn from_raw(handle: HWND) -> Self {
582 WindowHandle { handle }
583 }
584}
585
586impl WindowId {
587 pub unsafe fn handle(&self) -> usize {
588 self.handle
589 }
590
591 pub unsafe fn from_raw(handle: usize) -> Self {
592 WindowId { handle }
593 }
594}