-
Notifications
You must be signed in to change notification settings - Fork 160
Expand file tree
/
Copy pathnative_widgets_windows.c.v
More file actions
364 lines (317 loc) · 12.9 KB
/
native_widgets_windows.c.v
File metadata and controls
364 lines (317 loc) · 12.9 KB
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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
// Copyright (c) 2020-2025 Alexander Medvednikov. All rights reserved.
// Use of this source code is governed by a MIT license
// that can be found in the LICENSE file.
module ui
// Native Win32 widget bindings for Windows.
// Uses standard Win32 controls (BUTTON, EDIT, STATIC, msctls_progress32).
#flag windows -lgdi32
#flag windows -lcomctl32
fn C.GetParent(hwnd voidptr) voidptr
fn C.CreateWindowExW(ex_style u32, class_name &u16, window_name &u16, style u32, x int, y int, w int, h int, parent voidptr, menu voidptr, instance voidptr, param voidptr) voidptr
fn C.DestroyWindow(hwnd voidptr) bool
fn C.SetWindowTextW(hwnd voidptr, text &u16) bool
fn C.MoveWindow(hwnd voidptr, x int, y int, w int, h int, repaint bool) bool
fn C.SendMessageW(hwnd voidptr, msg u32, wparam usize, lparam isize) isize
fn C.GetWindowTextW(hwnd voidptr, buf &u16, max_count int) int
fn C.GetWindowTextLengthW(hwnd voidptr) int
fn C.EnableWindow(hwnd voidptr, enable bool) bool
fn C.ShowWindow(hwnd voidptr, cmd_show int) bool
fn C.IsWindowVisible(hwnd voidptr) bool
const ws_child = u32(0x40000000)
const ws_visible = u32(0x10000000)
const ws_tabstop = u32(0x00010000)
const ws_border = u32(0x00800000)
const ws_group = u32(0x00020000)
const es_autohscroll = u32(0x0080)
const es_password = u32(0x0020)
const bs_autocheckbox = u32(0x0003)
const bs_autoradiobutton = u32(0x0009)
const bs_pushbutton = u32(0x0000)
const bm_setcheck = u32(0x00F1)
const bm_getcheck = u32(0x00F0)
const bst_checked = usize(0x0001)
const bst_unchecked = usize(0x0000)
const pbm_setrange32 = u32(0x0406)
const pbm_setpos = u32(0x0402)
const sw_show = 5
const sw_hide = 0
const icc_progress_class = u32(0x00000020)
const wm_setfont = u32(0x0030)
const ss_left = u32(0x0000)
fn win32_button_class() &u16 {
return 'BUTTON'.to_wide()
}
fn win32_edit_class() &u16 {
return 'EDIT'.to_wide()
}
fn win32_static_class() &u16 {
return 'STATIC'.to_wide()
}
fn win32_progress_class() &u16 {
return 'msctls_progress32'.to_wide()
}
pub fn (mut nw NativeWidgets) init_parent(window_handle voidptr) {
nw.parent_handle = window_handle
}
pub fn (mut nw NativeWidgets) create_button(x int, y int, w int, h int, title string) NativeWidget {
handle := C.CreateWindowExW(0, win32_button_class(), title.to_wide(),
ws_child | ws_visible | ws_tabstop | bs_pushbutton, x, y, w, h, nw.parent_handle,
unsafe { nil }, unsafe { nil }, unsafe { nil })
return NativeWidget{
handle: handle
}
}
pub fn (nw &NativeWidgets) button_set_callback(nwidget &NativeWidget, callback fn (voidptr), v_button voidptr) {
}
pub fn (nw &NativeWidgets) update_button(nwidget &NativeWidget, x int, y int, w int, h int, title string) {
C.MoveWindow(nwidget.handle, x, y, w, h, true)
C.SetWindowTextW(nwidget.handle, title.to_wide())
}
pub fn (mut nw NativeWidgets) create_textfield(x int, y int, w int, h int, placeholder string) NativeWidget {
handle := C.CreateWindowExW(0, win32_edit_class(), ''.to_wide(),
ws_child | ws_visible | ws_tabstop | ws_border | es_autohscroll, x, y, w, h,
nw.parent_handle, unsafe { nil }, unsafe { nil }, unsafe { nil })
return NativeWidget{
handle: handle
}
}
pub fn (nw &NativeWidgets) update_textfield(nwidget &NativeWidget, x int, y int, w int, h int, text string, placeholder string) {
C.MoveWindow(nwidget.handle, x, y, w, h, true)
// Do NOT set text — it is owned by the native widget.
}
pub fn (nw &NativeWidgets) textfield_set_secure(nwidget &NativeWidget, secure bool) {
// Win32 password style must be set at creation time.
// This is a hint for future creation.
}
pub fn (mut nw NativeWidgets) create_checkbox(x int, y int, w int, h int, title string, checked bool) NativeWidget {
handle := C.CreateWindowExW(0, win32_button_class(), title.to_wide(),
ws_child | ws_visible | ws_tabstop | bs_autocheckbox, x, y, w, h, nw.parent_handle,
unsafe { nil }, unsafe { nil }, unsafe { nil })
if checked {
C.SendMessageW(handle, bm_setcheck, bst_checked, 0)
}
return NativeWidget{
handle: handle
}
}
pub fn (nw &NativeWidgets) update_checkbox(nwidget &NativeWidget, x int, y int, w int, h int, title string, checked bool) {
C.MoveWindow(nwidget.handle, x, y, w, h, true)
C.SetWindowTextW(nwidget.handle, title.to_wide())
// Do NOT set check state — it is owned by the native widget.
}
pub fn (mut nw NativeWidgets) create_radio_group(x int, y int, w int, h int, values []string, selected int, title string) NativeWidget {
// For Win32, we create individual radio buttons. We store the first radio's handle.
// The container is simulated via a group of controls at specific positions.
mut first_handle := unsafe { voidptr(nil) }
item_h := 20
for i, val in values {
style := ws_child | ws_visible | ws_tabstop | bs_autoradiobutton | if i == 0 {
ws_group
} else {
u32(0)
}
handle := C.CreateWindowExW(0, win32_button_class(), val.to_wide(), style, x,
y + i * item_h, w, item_h, nw.parent_handle, unsafe { nil }, unsafe { nil },
unsafe { nil })
if i == selected {
C.SendMessageW(handle, bm_setcheck, bst_checked, 0)
}
if i == 0 {
first_handle = handle
}
}
return NativeWidget{
handle: first_handle
}
}
pub fn (nw &NativeWidgets) update_radio_group(nwidget &NativeWidget, x int, y int, w int, h int, selected int) {
// Simplified: just move the first radio button. Full implementation would track all handles.
C.MoveWindow(nwidget.handle, x, y, w, h, true)
}
pub fn (mut nw NativeWidgets) create_progressbar(x int, y int, w int, h int, min f64, max f64, val f64) NativeWidget {
handle := C.CreateWindowExW(0, win32_progress_class(), ''.to_wide(), ws_child | ws_visible, x,
y, w, h, nw.parent_handle, unsafe { nil }, unsafe { nil }, unsafe { nil })
C.SendMessageW(handle, pbm_setrange32, usize(int(min)), isize(int(max)))
C.SendMessageW(handle, pbm_setpos, usize(int(val)), 0)
return NativeWidget{
handle: handle
}
}
pub fn (nw &NativeWidgets) update_progressbar(nwidget &NativeWidget, x int, y int, w int, h int, val f64) {
C.MoveWindow(nwidget.handle, x, y, w, h, true)
C.SendMessageW(nwidget.handle, pbm_setpos, usize(int(val)), 0)
}
pub fn (mut nw NativeWidgets) create_label(x int, y int, w int, h int, text string) NativeWidget {
handle := C.CreateWindowExW(0, win32_static_class(), text.to_wide(),
ws_child | ws_visible | ss_left, x, y, w, h, nw.parent_handle, unsafe { nil },
unsafe { nil }, unsafe { nil })
return NativeWidget{
handle: handle
}
}
pub fn (nw &NativeWidgets) update_label(nwidget &NativeWidget, x int, y int, w int, h int, text string) {
C.MoveWindow(nwidget.handle, x, y, w, h, true)
C.SetWindowTextW(nwidget.handle, text.to_wide())
}
// Win32 additional constants
const tbs_horz = u32(0x0000)
const tbs_vert = u32(0x0002)
const tbm_setrangemin = u32(0x0407)
const tbm_setrangemax = u32(0x0408)
const tbm_setpos_tb = u32(0x0405)
const cbs_dropdownlist = u32(0x0003)
const cb_addstring = u32(0x0143)
const cb_setcursel = u32(0x014E)
const lbs_notify = u32(0x0001)
const lbs_hasstrings = u32(0x0040)
const lb_addstring = u32(0x0180)
const lb_setcursel = u32(0x0186)
const ws_vscroll = u32(0x00200000)
const ss_bitmap = u32(0x000E)
const tbm_getpos = u32(0x0400)
const cb_getcursel = u32(0x0147)
const lb_getcursel = u32(0x0188)
fn win32_trackbar_class() &u16 {
return 'msctls_trackbar32'.to_wide()
}
fn win32_combobox_class() &u16 {
return 'COMBOBOX'.to_wide()
}
fn win32_listbox_class() &u16 {
return 'LISTBOX'.to_wide()
}
pub fn (mut nw NativeWidgets) create_slider(x int, y int, w int, h int, orientation Orientation, min f64, max f64, val f64) NativeWidget {
style := ws_child | ws_visible | ws_tabstop | if orientation == .horizontal {
tbs_horz
} else {
tbs_vert
}
handle := C.CreateWindowExW(0, win32_trackbar_class(), ''.to_wide(), style, x, y, w, h,
nw.parent_handle, unsafe { nil }, unsafe { nil }, unsafe { nil })
C.SendMessageW(handle, tbm_setrangemin, usize(0), isize(int(min)))
C.SendMessageW(handle, tbm_setrangemax, usize(0), isize(int(max)))
C.SendMessageW(handle, tbm_setpos_tb, usize(1), isize(int(val)))
return NativeWidget{
handle: handle
}
}
pub fn (nw &NativeWidgets) update_slider(nwidget &NativeWidget, x int, y int, w int, h int, val f64) {
C.MoveWindow(nwidget.handle, x, y, w, h, true)
// Do NOT set position — it is owned by the native widget.
}
pub fn (mut nw NativeWidgets) create_dropdown(x int, y int, w int, h int, items []string, selected int) NativeWidget {
handle := C.CreateWindowExW(0, win32_combobox_class(), ''.to_wide(),
ws_child | ws_visible | ws_tabstop | cbs_dropdownlist, x, y, w, h * 8, nw.parent_handle,
unsafe { nil }, unsafe { nil }, unsafe { nil })
for item in items {
C.SendMessageW(handle, cb_addstring, usize(0), isize(item.to_wide()))
}
if selected >= 0 {
C.SendMessageW(handle, cb_setcursel, usize(selected), 0)
}
return NativeWidget{
handle: handle
}
}
pub fn (nw &NativeWidgets) update_dropdown(nwidget &NativeWidget, x int, y int, w int, h int, selected int) {
C.MoveWindow(nwidget.handle, x, y, w, h * 8, true)
// Do NOT set selection — it is owned by the native widget.
}
pub fn (mut nw NativeWidgets) create_listbox(x int, y int, w int, h int, items []string, selected int) NativeWidget {
handle := C.CreateWindowExW(0, win32_listbox_class(), ''.to_wide(),
ws_child | ws_visible | ws_tabstop | ws_border | ws_vscroll | lbs_notify | lbs_hasstrings,
x, y, w, h, nw.parent_handle, unsafe { nil }, unsafe { nil }, unsafe { nil })
for item in items {
C.SendMessageW(handle, lb_addstring, usize(0), isize(item.to_wide()))
}
if selected >= 0 {
C.SendMessageW(handle, lb_setcursel, usize(selected), 0)
}
return NativeWidget{
handle: handle
}
}
pub fn (nw &NativeWidgets) update_listbox(nwidget &NativeWidget, x int, y int, w int, h int, selected int) {
C.MoveWindow(nwidget.handle, x, y, w, h, true)
// Do NOT set selection — it is owned by the native widget.
}
pub fn (mut nw NativeWidgets) create_switch(x int, y int, w int, h int, open bool) NativeWidget {
handle := C.CreateWindowExW(0, win32_button_class(), ''.to_wide(),
ws_child | ws_visible | ws_tabstop | bs_autocheckbox, x, y, w, h, nw.parent_handle,
unsafe { nil }, unsafe { nil }, unsafe { nil })
if open {
C.SendMessageW(handle, bm_setcheck, bst_checked, 0)
}
return NativeWidget{
handle: handle
}
}
pub fn (nw &NativeWidgets) update_switch(nwidget &NativeWidget, x int, y int, w int, h int, open bool) {
C.MoveWindow(nwidget.handle, x, y, w, h, true)
// Do NOT set check state — it is owned by the native widget.
}
pub fn (mut nw NativeWidgets) create_picture(x int, y int, w int, h int, path string) NativeWidget {
// Win32 STATIC with SS_BITMAP - simplified, just creates the control
handle := C.CreateWindowExW(0, win32_static_class(), ''.to_wide(),
ws_child | ws_visible | ss_bitmap, x, y, w, h, nw.parent_handle, unsafe { nil },
unsafe { nil }, unsafe { nil })
return NativeWidget{
handle: handle
}
}
pub fn (nw &NativeWidgets) update_picture(nwidget &NativeWidget, x int, y int, w int, h int) {
C.MoveWindow(nwidget.handle, x, y, w, h, true)
}
pub fn (mut nw NativeWidgets) create_menu(x int, y int, w int, h int, items []string) NativeWidget {
// Win32 menus are typically attached to the window frame via HMENU.
// For simplicity, we create button-based menu items.
mut first_handle := unsafe { voidptr(nil) }
count := items.len
item_w := if count > 0 { w / count } else { w }
for i, item in items {
handle := C.CreateWindowExW(0, win32_button_class(), item.to_wide(),
ws_child | ws_visible | bs_pushbutton, x + i * item_w, y, item_w, h, nw.parent_handle,
unsafe { nil }, unsafe { nil }, unsafe { nil })
if i == 0 {
first_handle = handle
}
}
return NativeWidget{
handle: first_handle
}
}
// -- Getters: read interactive state from native widgets --
pub fn (nw &NativeWidgets) textfield_get_text(nwidget &NativeWidget) string {
len := C.GetWindowTextLengthW(nwidget.handle)
if len <= 0 {
return ''
}
mut buf := []u16{len: len + 1}
C.GetWindowTextW(nwidget.handle, buf.data, len + 1)
return unsafe { string_from_wide(buf.data) }
}
pub fn (nw &NativeWidgets) checkbox_is_checked(nwidget &NativeWidget) bool {
return C.SendMessageW(nwidget.handle, bm_getcheck, 0, 0) == isize(bst_checked)
}
pub fn (nw &NativeWidgets) radio_get_selected(nwidget &NativeWidget) int {
// Simplified: check the first radio button only. Full implementation would track all handles.
if C.SendMessageW(nwidget.handle, bm_getcheck, 0, 0) == isize(bst_checked) {
return 0
}
return -1
}
pub fn (nw &NativeWidgets) slider_get_value(nwidget &NativeWidget) f64 {
return f64(C.SendMessageW(nwidget.handle, tbm_getpos, 0, 0))
}
pub fn (nw &NativeWidgets) dropdown_get_selected(nwidget &NativeWidget) int {
return int(C.SendMessageW(nwidget.handle, cb_getcursel, 0, 0))
}
pub fn (nw &NativeWidgets) listbox_get_selected(nwidget &NativeWidget) int {
return int(C.SendMessageW(nwidget.handle, lb_getcursel, 0, 0))
}
pub fn (nw &NativeWidgets) switch_is_open(nwidget &NativeWidget) bool {
return C.SendMessageW(nwidget.handle, bm_getcheck, 0, 0) == isize(bst_checked)
}
pub fn (nw &NativeWidgets) remove_widget(nwidget &NativeWidget) {
C.DestroyWindow(nwidget.handle)
}