1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
use {
compo::prelude::*,
tracing::{error, info},
windows::{
Win32::{
Foundation::{HWND, LPARAM, LRESULT, RECT, WPARAM},
Graphics::Gdi::HBRUSH,
System::LibraryLoader::GetModuleHandleW,
UI::{
Input::KeyboardAndMouse::EnableWindow,
WindowsAndMessaging::{
CREATESTRUCTW, CS_HREDRAW, CS_VREDRAW, CW_USEDEFAULT, CreateWindowExW,
DefWindowProcW, DestroyWindow, GWLP_USERDATA, GetClientRect, HCURSOR, HICON,
PostQuitMessage, RegisterClassW, SW_SHOW, SWP_NOZORDER, SetWindowLongPtrW,
SetWindowPos, SetWindowTextW, ShowWindow, WM_CREATE, WM_DESTROY, WNDCLASSW,
WS_EX_LEFT, WS_OVERLAPPEDWINDOW,
},
},
},
core::{PCWSTR, w},
},
};
// Window procedure callback function
unsafe extern "system" fn window_proc(
hwnd: HWND,
msg: u32,
wparam: WPARAM,
lparam: LPARAM,
) -> LRESULT {
match msg {
WM_CREATE => {
let create_struct = lparam.0 as *const CREATESTRUCTW;
let window_ptr = unsafe { (*create_struct).lpCreateParams };
unsafe { SetWindowLongPtrW(hwnd, GWLP_USERDATA, window_ptr as isize) };
LRESULT::default()
}
WM_DESTROY => {
unsafe { PostQuitMessage(0) };
LRESULT::default()
}
_ => unsafe { DefWindowProcW(hwnd, msg, wparam, lparam) },
}
}
// Window component
#[component]
pub async fn window(
#[default = "Window"] title: &str,
#[default = 800] width: i32,
#[default = 600] height: i32,
#[default = CW_USEDEFAULT] left: i32,
#[default = CW_USEDEFAULT] top: i32,
#[default = true] visible: bool,
#[default = true] enabled: bool,
) {
#[field]
// This is a field of the component's internal structure, not a variable in the current scope, so it can persist across multiple renders
let hwnd: Option<HWND> = None;
if *visible {
if hwnd.is_none() {
// Register window class
let h_instance = match unsafe { GetModuleHandleW(PCWSTR::null()) } {
Err(e) => {
error!(?e, "Can't get module handle.");
return;
}
Ok(h) => h.into(),
};
let class_name = w!("CompoWindow");
let wc = WNDCLASSW {
style: CS_HREDRAW | CS_VREDRAW,
lpfnWndProc: Some(window_proc),
cbClsExtra: 0,
cbWndExtra: 0,
hInstance: h_instance,
hIcon: HICON::default(),
hCursor: HCURSOR::default(),
hbrBackground: HBRUSH::default(),
lpszMenuName: PCWSTR::null(),
lpszClassName: class_name,
};
unsafe {
RegisterClassW(&wc);
// Create window
// Convert string to UTF-16 and ensure it ends with null
let mut window_title: Vec<u16> = title.encode_utf16().collect();
window_title.push(0); // Add null terminator
let hwnd_value = match CreateWindowExW(
WS_EX_LEFT,
class_name,
PCWSTR(window_title.as_ptr()),
WS_OVERLAPPEDWINDOW,
*left,
*top,
*width,
*height,
None,
None,
Some(h_instance),
None,
) {
Ok(h) => h,
Err(e) => {
error!(?e, "Can't create window.");
return;
}
};
hwnd.replace(hwnd_value);
}
}
// Show window and update parameters
if let Some(hwnd) = hwnd {
let _ = unsafe { ShowWindow(*hwnd, SW_SHOW) };
// Update window title (supports reactive updates)
let mut window_title: Vec<u16> = title.encode_utf16().collect();
window_title.push(0); // Add null terminator
let _ = unsafe { SetWindowTextW(*hwnd, PCWSTR(window_title.as_ptr())) };
// Update window position and size (supports reactive updates)
let _ = unsafe {
SetWindowPos(
*hwnd,
None,
*left,
*top,
*width,
*height,
SWP_NOZORDER, // Don't change Z-order
)
};
// Update window enabled state (supports reactive updates)
let _ = unsafe { EnableWindow(*hwnd, *enabled) };
// Get client area size
let mut rect = RECT {
left: 0,
top: 0,
right: 0,
bottom: 0,
};
let _ = unsafe { GetClientRect(*hwnd, &mut rect) };
info!(
"Window updated with client area: {}x{}",
rect.right - rect.left,
rect.bottom - rect.top
);
} else {
error!("Failed to create window.");
}
} else if let Some(hwnd) = hwnd.take() {
// If shown is false and window exists, destroy the window
let _ = unsafe { DestroyWindow(hwnd) };
}
}