quickwin 1.3.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +18 -24
- package/README.md +17 -23
- package/examples/PdfCanvas.js +8 -37
- package/examples/PdfCanvas.tsx +11 -41
- package/examples/exec_server.js +20155 -1420
- package/examples/exec_server.tsx +489 -0
- package/examples/exec_server_worker.js +383 -33
- package/examples/exec_server_worker.ts +408 -30
- package/examples/pdf_preview2.js +25 -82
- package/examples/pdf_preview2.ts +27 -84
- package/examples/pdf_viewer.js +1424 -1180
- package/examples/pdf_viewer.tsx +16 -67
- package/examples/setres.js +22 -47
- package/examples/setres.ts +23 -57
- package/examples/test_gallery.js +1418 -1075
- package/examples/test_react_lazy.js +1391 -1059
- package/examples/test_react_listbox.js +1390 -1058
- package/examples/test_root_window.js +1384 -1052
- package/examples/test_tab.js +1391 -1059
- package/lib/abort.js +70 -0
- package/lib/abort.ts +69 -0
- package/lib/certs.js +219 -0
- package/lib/certs.ts +229 -0
- package/lib/exec-log.js +15 -0
- package/lib/exec-log.ts +30 -0
- package/lib/fetch.js +88 -7
- package/lib/fetch.ts +73 -7
- package/lib/ffi/bind.js +441 -0
- package/lib/ffi/bind.ts +539 -0
- package/lib/ffi/ctype.js +194 -0
- package/lib/ffi/ctype.ts +369 -0
- package/lib/ffi/index.js +6 -0
- package/lib/ffi/index.ts +6 -0
- package/lib/ffi/struct.js +490 -0
- package/lib/ffi/struct.ts +753 -0
- package/lib/http-common.js +43 -5
- package/lib/http-common.ts +48 -6
- package/lib/http-server.js +187 -29
- package/lib/http-server.ts +177 -26
- package/lib/react-qw/components/DateTimePicker.js +18 -37
- package/lib/react-qw/components/Input.js +3 -10
- package/lib/react-qw/components/Link.js +6 -19
- package/lib/react-qw/components/ListView.js +50 -154
- package/lib/react-qw/components/PathPicker.js +28 -88
- package/lib/react-qw/components/ScrollView.js +15 -8
- package/lib/react-qw/components/Slider.js +2 -1
- package/lib/react-qw/components/Tab.js +6 -22
- package/lib/react-qw/components/Tooltip.js +10 -21
- package/lib/react-qw/components/TreeView.js +8 -31
- package/lib/react-qw/index.js +5 -3
- package/lib/react-qw/index.ts +8 -3
- package/lib/react-qw/nmhdr.js +3 -24
- package/lib/react-qw/nmhdr.ts +5 -30
- package/lib/react-qw/props.js +7 -2
- package/lib/react-qw/props.ts +7 -3
- package/lib/react-qw/reconciler.js +23 -13
- package/lib/react-qw/reconciler.ts +22 -13
- package/lib/text-measure.js +20 -23
- package/lib/text-measure.ts +22 -26
- package/lib/websocket.js +44 -4
- package/lib/websocket.ts +40 -4
- package/lib/windows/advapi32.js +53 -0
- package/lib/windows/advapi32.ts +64 -0
- package/lib/windows/comctl32.js +18 -0
- package/lib/windows/comctl32.ts +19 -0
- package/lib/windows/comdlg32.js +8 -0
- package/lib/windows/comdlg32.ts +10 -0
- package/lib/windows/gdi32.js +392 -0
- package/lib/windows/gdi32.ts +420 -0
- package/lib/windows/kernel32.js +262 -0
- package/lib/windows/kernel32.ts +291 -0
- package/lib/windows/msimg32.js +38 -0
- package/lib/windows/msimg32.ts +45 -0
- package/lib/windows/ntdll.js +9 -0
- package/lib/windows/ntdll.ts +11 -0
- package/lib/windows/ole32.js +7 -0
- package/lib/windows/ole32.ts +9 -0
- package/lib/windows/psapi.js +32 -0
- package/lib/windows/psapi.ts +37 -0
- package/lib/windows/shell32.js +13 -0
- package/lib/windows/shell32.ts +16 -0
- package/lib/windows/structs.js +519 -0
- package/lib/windows/structs.ts +566 -0
- package/lib/windows/user32.js +572 -0
- package/lib/windows/user32.ts +607 -0
- package/lib/windows/version.js +29 -0
- package/lib/windows/version.ts +35 -0
- package/lib/windows/winspool.js +71 -0
- package/lib/windows/winspool.ts +80 -0
- package/package.json +1 -1
- package/quickwin.d.ts +31 -40
- package/quickwin_const.d.ts +137 -0
- package/qwin-nowasm-x86.exe +0 -0
- package/qwin-nowasm.exe +0 -0
- package/qwin-x86.exe +0 -0
- package/qwin.exe +0 -0
- package/test/bench_ffi_callraw.js +236 -0
- package/test/bench_ffi_callraw.ts +236 -0
- package/test/bench_ffi_overhead.js +139 -0
- package/test/bench_ffi_overhead.ts +141 -0
- package/test/bench_sendmessage.js +104 -0
- package/test/bench_sendmessage.ts +100 -0
- package/test/bench_sendmessage_nonnull.js +187 -0
- package/test/bench_sendmessage_nonnull.ts +188 -0
- package/test/run.js +19 -4
- package/test/run.ts +20 -5
- package/test/test_basic_fetch.js +29 -0
- package/test/test_basic_fetch.ts +26 -0
- package/test/test_ffi.js +27 -45
- package/test/test_ffi.ts +35 -54
- package/test/test_ffi_abi.js +290 -0
- package/test/test_ffi_abi.ts +274 -0
- package/test/test_ffi_array.js +116 -0
- package/test/test_ffi_array.ts +141 -0
- package/test/test_ffi_bind.js +620 -8
- package/test/test_ffi_bind.ts +630 -8
- package/test/test_ffi_struct.js +632 -88
- package/test/test_ffi_struct.ts +666 -91
- package/test/test_ffi_struct_layout.js +495 -0
- package/test/test_ffi_struct_layout.ts +543 -0
- package/test/test_ffi_user32.js +103 -0
- package/test/test_ffi_user32.ts +113 -0
- package/test/test_ffi_windows.js +101 -0
- package/test/test_ffi_windows.ts +113 -0
- package/test/test_files.js +102 -0
- package/test/test_files.ts +121 -0
- package/test/test_gdi_draw.js +681 -0
- package/test/test_gdi_draw.ts +725 -0
- package/test/test_helper.js +8 -1
- package/test/test_helper.ts +9 -1
- package/test/test_http_import.js +18 -0
- package/test/test_http_import.ts +19 -0
- package/test/test_http_server.js +73 -1
- package/test/test_http_server.ts +75 -1
- package/test/test_ipv6.js +2 -2
- package/test/test_ipv6.ts +2 -2
- package/test/test_net_fetch.js +74 -11
- package/test/test_net_fetch.ts +73 -10
- package/test/test_net_websocket.js +1 -1
- package/test/test_net_websocket.ts +1 -1
- package/test/test_printer.js +137 -0
- package/test/test_printer.ts +148 -0
- package/test/test_proc.js +141 -0
- package/test/test_proc.ts +163 -0
- package/test/test_react_complex.js +1401 -1069
- package/test/test_react_counter.js +1391 -1059
- package/test/test_react_flex.js +1411 -1072
- package/test/test_react_hidden.js +1394 -1062
- package/test/test_react_listview_link.js +1391 -1059
- package/test/test_react_notify.js +18638 -0
- package/test/test_react_render.js +1391 -1059
- package/test/test_registry.js +74 -0
- package/test/test_registry.ts +95 -0
- package/test/test_sysinfo.js +122 -0
- package/test/test_sysinfo.ts +141 -0
- package/test/test_wolfssl_read.js +45 -43
- package/test/test_wolfssl_read.ts +42 -40
- package/test/test_worker_net.js +35 -17
- package/test/test_worker_net.ts +27 -15
- package/tsconfig.json +1 -1
- package/examples/exec_server.ts +0 -80
- package/lib/ffi-bind.js +0 -85
- package/lib/ffi-bind.ts +0 -170
- package/lib/ffi-struct.js +0 -224
- package/lib/ffi-struct.ts +0 -239
- package/lib/win-common-structs.js +0 -39
- package/lib/win-common-structs.ts +0 -41
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { NULL } from '../../ffi/ctype.js';
|
|
2
3
|
import { forwardRef, useRef, useEffect } from 'react';
|
|
3
4
|
import * as gui from 'gui';
|
|
4
5
|
import * as ffi from 'ffi';
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { WCHAR } from '../../ffi/bind.js';
|
|
7
|
+
import { nmCode } from '../nmhdr.js';
|
|
8
|
+
import { LoadCursor, SetCursor, ScreenToClient, GetCursorPos } from '../../windows/user32.js';
|
|
9
|
+
import { CreateFontIndirect, DeleteObject, GetObject, SelectObject } from '../../windows/gdi32.js';
|
|
10
|
+
import { LOGFONTW, POINT, NMCUSTOMDRAW, NMLVCUSTOMDRAW, NMLISTVIEW, LVHITTESTINFO, LVITEMW, LVCOLUMNW, } from '../../windows/structs.js';
|
|
8
11
|
export function makeColorBlock(size, bgra) {
|
|
9
12
|
const n = size * size;
|
|
10
13
|
const buf = new ArrayBuffer(n * 4);
|
|
@@ -17,106 +20,19 @@ export function makeColorBlock(size, bgra) {
|
|
|
17
20
|
}
|
|
18
21
|
return buf;
|
|
19
22
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const dv = new DataView(buf);
|
|
23
|
-
for (let i = 0; i < s.length; i++)
|
|
24
|
-
dv.setUint16(i * 2, s.charCodeAt(i), true);
|
|
25
|
-
return buf;
|
|
26
|
-
}
|
|
27
|
-
function readI32(ptr, offset) {
|
|
28
|
-
return ffi.readByte(ptr + offset) | (ffi.readByte(ptr + offset + 1) << 8) |
|
|
29
|
-
(ffi.readByte(ptr + offset + 2) << 16) | (ffi.readByte(ptr + offset + 3) << 24);
|
|
30
|
-
}
|
|
31
|
-
function readU32(ptr, offset) {
|
|
32
|
-
return ffi.readByte(ptr + offset) | (ffi.readByte(ptr + offset + 1) << 8) |
|
|
33
|
-
(ffi.readByte(ptr + offset + 2) << 16) | (ffi.readByte(ptr + offset + 3) << 24) >>> 0;
|
|
34
|
-
}
|
|
35
|
-
function readU64(ptr, offset) {
|
|
36
|
-
const lo = readU32(ptr, offset);
|
|
37
|
-
const hi = readU32(ptr, offset + 4);
|
|
38
|
-
return lo + hi * 0x100000000;
|
|
39
|
-
}
|
|
23
|
+
// 自绘时要把颜色写回通知结构指向的原生内存;struct 的 decode 能读原生指针、encode 只写 ArrayBuffer,
|
|
24
|
+
// 所以这两处写入保留逐字节写(偏移由 struct 的 offsetOf 给出)。
|
|
40
25
|
function writeU32(ptr, offset, v) {
|
|
41
26
|
ffi.writeByte(ptr + offset, v & 0xFF);
|
|
42
27
|
ffi.writeByte(ptr + offset + 1, (v >> 8) & 0xFF);
|
|
43
28
|
ffi.writeByte(ptr + offset + 2, (v >> 16) & 0xFF);
|
|
44
29
|
ffi.writeByte(ptr + offset + 3, (v >> 24) & 0xFF);
|
|
45
30
|
}
|
|
46
|
-
function bufPtr(buf) {
|
|
47
|
-
return ffi.bufferPtr(buf);
|
|
48
|
-
}
|
|
49
31
|
const LV_WS = 268435456 /* gui.WindowStyle.VISIBLE */ | 8388608 /* gui.WindowStyle.BORDER */ | 2097152 /* gui.WindowStyle.VSCROLL */ | 1048576 /* gui.WindowStyle.HSCROLL */
|
|
50
32
|
| 1 /* gui.ListViewStyle.REPORT */ | 4 /* gui.ListViewStyle.SINGLESEL */;
|
|
51
|
-
// ListView 通知结构(嵌套 NMHDR 以复现 MSVC 尾 padding 传染,offsetOf 保证 ia32/x64 均正确)。
|
|
52
|
-
const NMCUSTOMDRAW = struct({
|
|
53
|
-
hdr: NMHDR,
|
|
54
|
-
dwDrawStage: 'u32', hdc: 'ptr', rc: 'i32[4]',
|
|
55
|
-
dwItemSpec: 'ptr', uItemState: 'u32', lItemlParam: 'ptr',
|
|
56
|
-
});
|
|
57
|
-
const NMLVCUSTOMDRAW = struct({
|
|
58
|
-
hdr: NMHDR,
|
|
59
|
-
dwDrawStage: 'u32', hdc: 'ptr', rc: 'i32[4]',
|
|
60
|
-
dwItemSpec: 'ptr', uItemState: 'u32', lItemlParam: 'ptr',
|
|
61
|
-
clrText: 'u32', clrTextBk: 'u32', iSubItem: 'i32', dwItemType: 'u32',
|
|
62
|
-
});
|
|
63
|
-
// NMLISTVIEW / NMITEMACTIVATE 前缀字段布局一致(iItem..uChanged)
|
|
64
|
-
const NMLISTVIEW = struct({
|
|
65
|
-
hdr: NMHDR,
|
|
66
|
-
iItem: 'i32', iSubItem: 'i32', uNewState: 'u32', uOldState: 'u32', uChanged: 'u32',
|
|
67
|
-
ptAction: 'i32[2]', lParam: 'ptr',
|
|
68
|
-
});
|
|
69
|
-
const CD_STAGE = NMCUSTOMDRAW.offsetOf('dwDrawStage');
|
|
70
|
-
const CD_HDC = NMCUSTOMDRAW.offsetOf('hdc');
|
|
71
|
-
const CD_ITEM = NMCUSTOMDRAW.offsetOf('dwItemSpec');
|
|
72
33
|
const CD_CLRTEXT = NMLVCUSTOMDRAW.offsetOf('clrText');
|
|
73
34
|
const CD_CLRTEXTBK = NMLVCUSTOMDRAW.offsetOf('clrTextBk');
|
|
74
|
-
const CD_SUBITEM = NMLVCUSTOMDRAW.offsetOf('iSubItem');
|
|
75
|
-
const NMIA_ITEM = NMLISTVIEW.offsetOf('iItem');
|
|
76
|
-
const NMIA_SUBITEM = NMLISTVIEW.offsetOf('iSubItem');
|
|
77
|
-
const NMLV_UNEW = NMLISTVIEW.offsetOf('uNewState');
|
|
78
|
-
const NMLV_UOLD = NMLISTVIEW.offsetOf('uOldState');
|
|
79
|
-
const LVITEMW = struct({
|
|
80
|
-
mask: 'u32', iItem: 'i32', iSubItem: 'i32',
|
|
81
|
-
state: 'u32', stateMask: 'u32',
|
|
82
|
-
pszText: 'ptr', cchTextMax: 'i32', iImage: 'i32', lParam: 'ptr',
|
|
83
|
-
iIndent: 'i32', iGroupId: 'i32', cColumns: 'u32',
|
|
84
|
-
puColumns: 'ptr', piColFmt: 'ptr', iGroup: 'i32',
|
|
85
|
-
});
|
|
86
|
-
const LVCOLUMNW = struct({
|
|
87
|
-
mask: 'u32', fmt: 'i32', cx: 'i32',
|
|
88
|
-
pszText: 'ptr', cchTextMax: 'i32', iSubItem: 'i32', iImage: 'i32', iOrder: 'i32',
|
|
89
|
-
cxMin: 'i32', cxDefault: 'i32', cxIdeal: 'i32',
|
|
90
|
-
});
|
|
91
35
|
const fontCache = new Map();
|
|
92
|
-
let gdi32 = null;
|
|
93
|
-
let createFontIndirectW = null;
|
|
94
|
-
let selectObjectFn = null;
|
|
95
|
-
let getObjectW = null;
|
|
96
|
-
function ensureGdi() {
|
|
97
|
-
if (gdi32 !== null)
|
|
98
|
-
return;
|
|
99
|
-
gdi32 = win.LoadLibrary('gdi32.dll');
|
|
100
|
-
if (!gdi32)
|
|
101
|
-
return;
|
|
102
|
-
createFontIndirectW = win.GetProcAddress(gdi32, 'CreateFontIndirectW');
|
|
103
|
-
selectObjectFn = win.GetProcAddress(gdi32, 'SelectObject');
|
|
104
|
-
getObjectW = win.GetProcAddress(gdi32, 'GetObjectW');
|
|
105
|
-
}
|
|
106
|
-
let user32 = null;
|
|
107
|
-
let loadCursorW = null;
|
|
108
|
-
let setCursorFn = null;
|
|
109
|
-
let screenToClient = null;
|
|
110
|
-
function ensureUser32() {
|
|
111
|
-
if (user32 !== null)
|
|
112
|
-
return;
|
|
113
|
-
user32 = win.LoadLibrary('user32.dll');
|
|
114
|
-
if (!user32)
|
|
115
|
-
return;
|
|
116
|
-
loadCursorW = win.GetProcAddress(user32, 'LoadCursorW');
|
|
117
|
-
setCursorFn = win.GetProcAddress(user32, 'SetCursor');
|
|
118
|
-
screenToClient = win.GetProcAddress(user32, 'ScreenToClient');
|
|
119
|
-
}
|
|
120
36
|
function getCellFont(hwnd, style) {
|
|
121
37
|
const key = (style.bold ? 'b' : '') + (style.italic ? 'i' : '') + (style.underline ? 'u' : '');
|
|
122
38
|
if (key === '')
|
|
@@ -124,40 +40,37 @@ function getCellFont(hwnd, style) {
|
|
|
124
40
|
const cached = fontCache.get(key);
|
|
125
41
|
if (cached !== undefined)
|
|
126
42
|
return cached === 0 ? null : cached;
|
|
127
|
-
|
|
128
|
-
if (!createFontIndirectW || !getObjectW || !hwnd)
|
|
43
|
+
if (!hwnd)
|
|
129
44
|
return null;
|
|
130
|
-
|
|
131
|
-
|
|
45
|
+
// 以控件当前字体为底(取不到则用 -13 高度的默认字体),再叠加粗 / 斜 / 下划线
|
|
46
|
+
let base = { lfHeight: -13 };
|
|
132
47
|
const cur = gui.SendMessage(hwnd, 49 /* gui.WmMsg.GETFONT */, 0, 0);
|
|
133
48
|
if (cur) {
|
|
134
|
-
const
|
|
135
|
-
|
|
49
|
+
const lf = LOGFONTW.alloc();
|
|
50
|
+
// GETFONT 的 LRESULT 断言为 HFONT(gdi32 <HGDIOBJ>ptr 位)
|
|
51
|
+
if (!GetObject(cur, LOGFONTW.size, lf))
|
|
136
52
|
return null;
|
|
53
|
+
base = LOGFONTW.decode(lf);
|
|
137
54
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
dv.setUint8(20, 1);
|
|
145
|
-
if (style.underline)
|
|
146
|
-
dv.setUint8(21, 1);
|
|
147
|
-
const h = ffi.ffiCall(createFontIndirectW, [ffi.FFI_TYPE_POINTER], [lf], ffi.FFI_TYPE_POINTER);
|
|
55
|
+
const h = CreateFontIndirect({
|
|
56
|
+
...base,
|
|
57
|
+
...(style.bold ? { lfWeight: 700 /* gui.FontWeight.BOLD */ } : {}),
|
|
58
|
+
...(style.italic ? { lfItalic: 1 } : {}),
|
|
59
|
+
...(style.underline ? { lfUnderline: 1 } : {}),
|
|
60
|
+
});
|
|
148
61
|
fontCache.set(key, h ? h : 0);
|
|
149
62
|
return h ? h : null;
|
|
150
63
|
}
|
|
151
64
|
function handleCustomDraw(lParam, columns, data, hwnd) {
|
|
152
|
-
const stage =
|
|
65
|
+
const { dwDrawStage: stage } = NMCUSTOMDRAW.decode(lParam);
|
|
153
66
|
if (stage === 1 /* gui.CustomDrawStage.PREPAINT */)
|
|
154
67
|
return 32 /* gui.CustomDrawFlag.NOTIFYITEMDRAW */;
|
|
155
68
|
if (stage === 65537 /* gui.CustomDrawStage.ITEMPREPAINT */)
|
|
156
69
|
return 32 /* gui.CustomDrawFlag.NOTIFYSUBITEMDRAW */;
|
|
157
70
|
if (stage === 196609 /* gui.CustomDrawStage.SUBITEMPREPAINT */) {
|
|
158
|
-
|
|
159
|
-
const
|
|
160
|
-
const style = resolveCellStyle(columns, data,
|
|
71
|
+
// 只有子项阶段才按 NMLVCUSTOMDRAW(更大的结构)读,前面的阶段只读了 NMCUSTOMDRAW 的前缀
|
|
72
|
+
const cd = NMLVCUSTOMDRAW.decode(lParam);
|
|
73
|
+
const style = resolveCellStyle(columns, data, cd.dwItemSpec, cd.iSubItem);
|
|
161
74
|
if (!style)
|
|
162
75
|
return 0 /* gui.CustomDrawFlag.DODEFAULT */;
|
|
163
76
|
if (style.color !== undefined)
|
|
@@ -165,12 +78,9 @@ function handleCustomDraw(lParam, columns, data, hwnd) {
|
|
|
165
78
|
if (style.background !== undefined)
|
|
166
79
|
writeU32(lParam, CD_CLRTEXTBK, style.background);
|
|
167
80
|
const hfont = getCellFont(hwnd, style);
|
|
168
|
-
if (hfont &&
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
ffi.ffiCall(selectObjectFn, [ffi.FFI_TYPE_POINTER, ffi.FFI_TYPE_POINTER], [hdc, hfont], ffi.FFI_TYPE_POINTER);
|
|
172
|
-
return 2 /* gui.CustomDrawFlag.NEWFONT */;
|
|
173
|
-
}
|
|
81
|
+
if (hfont && cd.hdc) {
|
|
82
|
+
SelectObject(cd.hdc, hfont);
|
|
83
|
+
return 2 /* gui.CustomDrawFlag.NEWFONT */;
|
|
174
84
|
}
|
|
175
85
|
}
|
|
176
86
|
return 0 /* gui.CustomDrawFlag.DODEFAULT */;
|
|
@@ -201,14 +111,14 @@ function resolveCellStyle(columns, data, row, colIndex) {
|
|
|
201
111
|
return style || undefined;
|
|
202
112
|
}
|
|
203
113
|
function makeLVItem(i, sub, text, image) {
|
|
204
|
-
const textBuf =
|
|
205
|
-
const b = LVITEMW.
|
|
114
|
+
const textBuf = WCHAR.encode(text);
|
|
115
|
+
const b = LVITEMW.encode({
|
|
206
116
|
mask: 1 /* LvItemFlag.TEXT */ | (image !== undefined ? 2 /* LvItemFlag.IMAGE */ : 0),
|
|
207
117
|
iItem: i,
|
|
208
118
|
iSubItem: sub,
|
|
209
119
|
state: 0,
|
|
210
120
|
stateMask: 0,
|
|
211
|
-
pszText:
|
|
121
|
+
pszText: textBuf.ptr,
|
|
212
122
|
cchTextMax: 0,
|
|
213
123
|
iImage: image ?? 0,
|
|
214
124
|
lParam: 0,
|
|
@@ -245,7 +155,7 @@ const ListView = forwardRef(function ListViewInner({ columns, data, style, icons
|
|
|
245
155
|
const hbm = gui.CreateBitmapFromPixels(size, size, p);
|
|
246
156
|
if (hbm) {
|
|
247
157
|
gui.ImageListAdd(img, hbm);
|
|
248
|
-
|
|
158
|
+
DeleteObject(hbm);
|
|
249
159
|
}
|
|
250
160
|
}
|
|
251
161
|
gui.SendMessage(h, 4099 /* gui.LvMsg.SETIMAGELIST */, 1 /* gui.LvImageList.SMALL */, img);
|
|
@@ -265,12 +175,12 @@ const ListView = forwardRef(function ListViewInner({ columns, data, style, icons
|
|
|
265
175
|
gui.SendMessage(h, 4150 /* gui.LvMsg.SETEXTENDEDLISTVIEWSTYLE */, 0, 32 /* gui.LvExStyle.FULLROWSELECT */ | 1 /* gui.LvExStyle.GRIDLINES */ | 65536 /* gui.LvExStyle.DOUBLEBUFFER */);
|
|
266
176
|
const n = columns.length;
|
|
267
177
|
for (let j = 0; j < n; j++) {
|
|
268
|
-
const titleBuf =
|
|
269
|
-
const lvc = LVCOLUMNW.
|
|
178
|
+
const titleBuf = WCHAR.encode(columns[j].name);
|
|
179
|
+
const lvc = LVCOLUMNW.encode({
|
|
270
180
|
mask: 4 /* LvColumnMask.TEXT */ | 2 /* LvColumnMask.WIDTH */ | 1 /* LvColumnMask.FORMAT */,
|
|
271
181
|
fmt: alignToFmt(columns[j].align),
|
|
272
182
|
cx: columns[j].width ?? 100,
|
|
273
|
-
pszText:
|
|
183
|
+
pszText: titleBuf.ptr,
|
|
274
184
|
cchTextMax: 0,
|
|
275
185
|
iSubItem: j,
|
|
276
186
|
iImage: 0,
|
|
@@ -280,7 +190,7 @@ const ListView = forwardRef(function ListViewInner({ columns, data, style, icons
|
|
|
280
190
|
cxIdeal: 0,
|
|
281
191
|
});
|
|
282
192
|
lvc.__titleBuf = titleBuf;
|
|
283
|
-
gui.SendMessage(h, 4193 /* gui.LvMsg.INSERTCOLUMNW */, j,
|
|
193
|
+
gui.SendMessage(h, 4193 /* gui.LvMsg.INSERTCOLUMNW */, j, lvc.ptr);
|
|
284
194
|
}
|
|
285
195
|
}, [columns]);
|
|
286
196
|
useEffect(() => {
|
|
@@ -293,10 +203,10 @@ const ListView = forwardRef(function ListViewInner({ columns, data, style, icons
|
|
|
293
203
|
const record = data[i];
|
|
294
204
|
const img = getIcon ? getIcon(record, i) : undefined;
|
|
295
205
|
const itemBuf = makeLVItem(i, 0, cellText(record, columns[0], i), img);
|
|
296
|
-
gui.SendMessage(h, 4173 /* gui.LvMsg.INSERTITEMW */, 0,
|
|
206
|
+
gui.SendMessage(h, 4173 /* gui.LvMsg.INSERTITEMW */, 0, itemBuf.ptr);
|
|
297
207
|
for (let j = 1; j < nCols; j++) {
|
|
298
208
|
const subBuf = makeLVItem(i, j, cellText(record, columns[j], i));
|
|
299
|
-
gui.SendMessage(h, 4172 /* gui.LvMsg.SETITEMW */, 0,
|
|
209
|
+
gui.SendMessage(h, 4172 /* gui.LvMsg.SETITEMW */, 0, subBuf.ptr);
|
|
300
210
|
}
|
|
301
211
|
}
|
|
302
212
|
for (let j = 0; j < columns.length; j++) {
|
|
@@ -312,14 +222,12 @@ const ListView = forwardRef(function ListViewInner({ columns, data, style, icons
|
|
|
312
222
|
return handleCustomDraw(e.lParam, columns, data, lvRef.current);
|
|
313
223
|
}
|
|
314
224
|
if (code === -100 /* gui.LvNotifyCode.ITEMCHANGING */) {
|
|
315
|
-
const uNewState =
|
|
316
|
-
const uOldState = readU32(e.lParam, NMLV_UOLD);
|
|
225
|
+
const { uNewState, uOldState } = NMLISTVIEW.decode(e.lParam);
|
|
317
226
|
if ((uNewState & 2 /* LvItemState.SELECTED */) !== (uOldState & 2 /* LvItemState.SELECTED */))
|
|
318
227
|
return 1;
|
|
319
228
|
}
|
|
320
229
|
if (code === -2 /* gui.LvNotifyCode.CLICK */) {
|
|
321
|
-
const iItem =
|
|
322
|
-
const iSubItem = readI32(e.lParam, NMIA_SUBITEM);
|
|
230
|
+
const { iItem, iSubItem } = NMLISTVIEW.decode(e.lParam);
|
|
323
231
|
const col = columns[iSubItem];
|
|
324
232
|
const record = data[iItem];
|
|
325
233
|
if (col?.onCellClick && record !== undefined)
|
|
@@ -337,33 +245,21 @@ const ListView = forwardRef(function ListViewInner({ columns, data, style, icons
|
|
|
337
245
|
const h = lvRef.current;
|
|
338
246
|
if (!h)
|
|
339
247
|
return;
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
const sp = gui.GetCursorPos();
|
|
344
|
-
if (!sp)
|
|
248
|
+
// GetCursorPos 出参写入初值 → ScreenToClient 就地更新 → decode 读回
|
|
249
|
+
const pt = POINT.alloc();
|
|
250
|
+
if (!GetCursorPos(pt.ptr))
|
|
345
251
|
return;
|
|
346
|
-
|
|
347
|
-
const
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
const lvhi = new ArrayBuffer(24);
|
|
352
|
-
const lvd = new DataView(lvhi);
|
|
353
|
-
lvd.setInt32(0, sdv.getInt32(0, true), true);
|
|
354
|
-
lvd.setInt32(4, sdv.getInt32(4, true), true);
|
|
355
|
-
lvd.setInt32(12, -1, true);
|
|
356
|
-
lvd.setInt32(16, -1, true);
|
|
357
|
-
const lvhiPtr = bufPtr(lvhi);
|
|
358
|
-
gui.SendMessage(h, 4153 /* gui.LvMsg.SUBITEMHITTEST */, 0, lvhiPtr);
|
|
359
|
-
const iItem = readI32(lvhiPtr, 12);
|
|
360
|
-
const iSubItem = readI32(lvhiPtr, 16);
|
|
252
|
+
ScreenToClient(h, pt.ptr);
|
|
253
|
+
const { x: sx, y: sy } = POINT.decode(pt);
|
|
254
|
+
const hit = LVHITTESTINFO.encode({ pt: [sx, sy], iItem: -1, iSubItem: -1 });
|
|
255
|
+
gui.SendMessage(h, 4153 /* gui.LvMsg.SUBITEMHITTEST */, 0, hit.ptr);
|
|
256
|
+
const { iItem, iSubItem } = LVHITTESTINFO.decode(hit);
|
|
361
257
|
const style = resolveCellStyle(columns, data, iItem, iSubItem);
|
|
362
258
|
if (!style || style.cursor === undefined)
|
|
363
259
|
return;
|
|
364
|
-
const hc =
|
|
260
|
+
const hc = LoadCursor(NULL, style.cursor);
|
|
365
261
|
if (hc) {
|
|
366
|
-
|
|
262
|
+
SetCursor(hc);
|
|
367
263
|
return 1;
|
|
368
264
|
}
|
|
369
265
|
return;
|
|
@@ -1,114 +1,54 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { forwardRef, useState, useRef } from 'react';
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
let _GetOpenFileNameW = 0;
|
|
9
|
-
let _SHBrowseForFolderW = 0;
|
|
10
|
-
let _SHGetPathFromIDListW = 0;
|
|
11
|
-
let _CoTaskMemFree = 0;
|
|
12
|
-
function ensureDlls() {
|
|
13
|
-
if (_GetOpenFileNameW)
|
|
14
|
-
return true;
|
|
15
|
-
const comdlg32 = win.LoadLibrary('comdlg32.dll');
|
|
16
|
-
const shell32 = win.LoadLibrary('shell32.dll');
|
|
17
|
-
const ole32 = win.LoadLibrary('ole32.dll');
|
|
18
|
-
if (!comdlg32 || !shell32 || !ole32)
|
|
19
|
-
return false;
|
|
20
|
-
const load = (lib, name) => win.GetProcAddress(lib, name) || 0;
|
|
21
|
-
_GetOpenFileNameW = load(comdlg32, 'GetOpenFileNameW');
|
|
22
|
-
_SHBrowseForFolderW = load(shell32, 'SHBrowseForFolderW');
|
|
23
|
-
_SHGetPathFromIDListW = load(shell32, 'SHGetPathFromIDListW');
|
|
24
|
-
_CoTaskMemFree = load(ole32, 'CoTaskMemFree');
|
|
25
|
-
return !!_GetOpenFileNameW && !!_SHBrowseForFolderW && !!_SHGetPathFromIDListW && !!_CoTaskMemFree;
|
|
26
|
-
}
|
|
27
|
-
function strToWide(s) {
|
|
28
|
-
return new TextEncoder('utf-16le').encode(s + '\0').buffer;
|
|
29
|
-
}
|
|
30
|
-
const _decoder = new TextDecoder('utf-16le');
|
|
31
|
-
function wideToStr(buf, offset = 0) {
|
|
32
|
-
const str = _decoder.decode(new Uint8Array(buf, offset));
|
|
33
|
-
const nullIdx = str.indexOf('\0');
|
|
34
|
-
return nullIdx >= 0 ? str.substring(0, nullIdx) : str;
|
|
35
|
-
}
|
|
3
|
+
import { WCHAR } from '../../ffi/bind.js';
|
|
4
|
+
import { OPENFILENAMEW, BROWSEINFOW } from '../../windows/structs.js';
|
|
5
|
+
import { GetOpenFileName } from '../../windows/comdlg32.js';
|
|
6
|
+
import { SHBrowseForFolder, SHGetPathFromIDList } from '../../windows/shell32.js';
|
|
7
|
+
import { CoTaskMemFree } from '../../windows/ole32.js';
|
|
36
8
|
function openFileDialog(owner, filter, title, multiple) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const
|
|
40
|
-
const filterWide = strToWide(filter);
|
|
41
|
-
const titleWide = title ? strToWide(title) : null;
|
|
9
|
+
const fileBuf = WCHAR.alloc(260);
|
|
10
|
+
const filterWide = WCHAR.encode(filter);
|
|
11
|
+
const titleWide = title ? WCHAR.encode(title) : null;
|
|
42
12
|
let flags = 0x1000 | 0x0800 | 0x0008;
|
|
43
13
|
if (multiple)
|
|
44
14
|
flags |= 0x0200;
|
|
45
15
|
flags |= 0x80000;
|
|
46
|
-
|
|
16
|
+
// 缺省字段跳过 = fresh buffer 上留 0
|
|
17
|
+
const ofn = OPENFILENAMEW.encode({
|
|
47
18
|
lStructSize: OPENFILENAMEW.size,
|
|
48
19
|
hwndOwner: owner,
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
lpstrCustomFilter: 0,
|
|
52
|
-
nMaxCustFilter: 0,
|
|
53
|
-
nFilterIndex: 0,
|
|
54
|
-
lpstrFile: ffi.bufferPtr(fileBuf),
|
|
20
|
+
lpstrFilter: filterWide.ptr,
|
|
21
|
+
lpstrFile: fileBuf.ptr,
|
|
55
22
|
nMaxFile: 260,
|
|
56
|
-
|
|
57
|
-
nMaxFileTitle: 0,
|
|
58
|
-
lpstrInitialDir: 0,
|
|
59
|
-
lpstrTitle: titleWide ? ffi.bufferPtr(titleWide) : 0,
|
|
23
|
+
lpstrTitle: titleWide?.ptr ?? 0,
|
|
60
24
|
Flags: flags,
|
|
61
|
-
nFileOffset: 0,
|
|
62
|
-
nFileExtension: 0,
|
|
63
|
-
lpstrDefExt: 0,
|
|
64
|
-
lCustData: 0,
|
|
65
|
-
lpfnHook: 0,
|
|
66
|
-
lpTemplateName: 0,
|
|
67
|
-
pvReserved: 0,
|
|
68
|
-
dwReserved: 0,
|
|
69
|
-
FlagsEx: 0,
|
|
70
25
|
});
|
|
71
|
-
const ret =
|
|
26
|
+
const ret = GetOpenFileName(ofn.ptr);
|
|
72
27
|
if (!ret)
|
|
73
28
|
return null;
|
|
74
29
|
if (!multiple) {
|
|
75
|
-
return
|
|
76
|
-
}
|
|
77
|
-
const dir = wideToStr(fileBuf);
|
|
78
|
-
let pos = (dir.length + 1) * 2;
|
|
79
|
-
const files = [];
|
|
80
|
-
while (pos < fileBuf.byteLength) {
|
|
81
|
-
const f = wideToStr(fileBuf, pos);
|
|
82
|
-
if (f.length === 0)
|
|
83
|
-
break;
|
|
84
|
-
files.push(dir + '\\' + f);
|
|
85
|
-
pos += (f.length + 1) * 2;
|
|
30
|
+
return WCHAR.decode(fileBuf);
|
|
86
31
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
32
|
+
// 多选缓冲:dir\0 file1\0 file2\0 \0 —— 整缓冲一次解码按 NUL 拆分
|
|
33
|
+
const strs = new TextDecoder('utf-16le').decode(new Uint8Array(fileBuf)).split('\0');
|
|
34
|
+
const dir = strs[0] ?? '';
|
|
35
|
+
const files = strs.slice(1).filter((f) => f.length > 0).map((f) => dir + '\\' + f);
|
|
36
|
+
return files.length > 0 ? files : [dir];
|
|
90
37
|
}
|
|
91
38
|
function openFolderDialog(owner, title) {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const titleWide = title ? strToWide(title) : null;
|
|
95
|
-
const bi = BROWSEINFOW.write({
|
|
39
|
+
const titleWide = title ? WCHAR.encode(title) : null;
|
|
40
|
+
const bi = BROWSEINFOW.encode({
|
|
96
41
|
hwndOwner: owner,
|
|
97
|
-
|
|
98
|
-
pszDisplayName: 0,
|
|
99
|
-
lpszTitle: titleWide ? ffi.bufferPtr(titleWide) : 0,
|
|
42
|
+
lpszTitle: titleWide?.ptr ?? 0,
|
|
100
43
|
ulFlags: 0x00000041,
|
|
101
|
-
lpfn: 0,
|
|
102
|
-
lParam: 0,
|
|
103
|
-
iImage: 0,
|
|
104
44
|
});
|
|
105
|
-
const pidl =
|
|
45
|
+
const pidl = SHBrowseForFolder(bi.ptr);
|
|
106
46
|
if (!pidl)
|
|
107
47
|
return null;
|
|
108
|
-
const pathBuf =
|
|
109
|
-
const ok =
|
|
110
|
-
|
|
111
|
-
return ok ?
|
|
48
|
+
const pathBuf = WCHAR.alloc(260);
|
|
49
|
+
const ok = SHGetPathFromIDList(pidl, pathBuf);
|
|
50
|
+
CoTaskMemFree(pidl);
|
|
51
|
+
return ok ? WCHAR.decode(pathBuf) : null;
|
|
112
52
|
}
|
|
113
53
|
export const PathPicker = forwardRef((props, ref) => {
|
|
114
54
|
const { type = 'file', filter = 'All Files\0*.*\0\0', title, disabled, style } = props;
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { forwardRef, useLayoutEffect, useRef } from 'react';
|
|
3
3
|
import * as gui from 'gui';
|
|
4
|
+
import { DefWindowProc, SetWindowPos, ShowScrollBar, GetDC, ReleaseDC } from '../../windows/user32.js';
|
|
5
|
+
import { NULL } from '../../ffi/ctype.js';
|
|
6
|
+
import { GetDeviceCaps } from '../../windows/gdi32.js';
|
|
4
7
|
import { forceFlexLayout } from '../reconciler.js';
|
|
5
8
|
export const ScrollView = forwardRef(({ children, style, contentWidth, contentHeight }, ref) => {
|
|
6
9
|
const svRef = useRef(null);
|
|
@@ -18,28 +21,32 @@ export const ScrollView = forwardRef(({ children, style, contentWidth, contentHe
|
|
|
18
21
|
return;
|
|
19
22
|
const svW = svRect.right - svRect.left;
|
|
20
23
|
const svH = svRect.bottom - svRect.top;
|
|
21
|
-
|
|
24
|
+
// GetScaleFactor 等价内联:GetDC → GetDeviceCaps(LOGPIXELSX=88) → ReleaseDC 配对
|
|
25
|
+
const hdc = GetDC(NULL);
|
|
26
|
+
const scale = hdc ? GetDeviceCaps(hdc, 88) / 96 : 1;
|
|
27
|
+
if (hdc)
|
|
28
|
+
ReleaseDC(NULL, hdc);
|
|
22
29
|
const natW = Math.round((contentWidth ?? (svW / scale)) * scale);
|
|
23
30
|
const natH = Math.round((contentHeight ?? (svH / scale)) * scale);
|
|
24
31
|
scrollXRef.current = Math.max(0, Math.min(scrollXRef.current, natW - svW));
|
|
25
32
|
scrollYRef.current = Math.max(0, Math.min(scrollYRef.current, natH - svH));
|
|
26
|
-
|
|
33
|
+
SetWindowPos(content, 0 /* gui.SetWindowPosHwnd.TOP */, -scrollXRef.current, -scrollYRef.current, natW, natH, 4 /* gui.SetWindowPosFlag.SWP_NOZORDER */);
|
|
27
34
|
forceFlexLayout(content);
|
|
28
35
|
if (natW > svW) {
|
|
29
36
|
gui.SetScrollInfo(sv, 0 /* gui.ScrollBar.HORZ */, { min: 0, max: natW - 1, page: svW, pos: scrollXRef.current }, true);
|
|
30
|
-
|
|
37
|
+
ShowScrollBar(sv, 0 /* gui.ScrollBar.HORZ */, 1);
|
|
31
38
|
}
|
|
32
39
|
else {
|
|
33
40
|
scrollXRef.current = 0;
|
|
34
|
-
|
|
41
|
+
ShowScrollBar(sv, 0 /* gui.ScrollBar.HORZ */, 0);
|
|
35
42
|
}
|
|
36
43
|
if (natH > svH) {
|
|
37
44
|
gui.SetScrollInfo(sv, 1 /* gui.ScrollBar.VERT */, { min: 0, max: natH - 1, page: svH, pos: scrollYRef.current }, true);
|
|
38
|
-
|
|
45
|
+
ShowScrollBar(sv, 1 /* gui.ScrollBar.VERT */, 1);
|
|
39
46
|
}
|
|
40
47
|
else {
|
|
41
48
|
scrollYRef.current = 0;
|
|
42
|
-
|
|
49
|
+
ShowScrollBar(sv, 1 /* gui.ScrollBar.VERT */, 0);
|
|
43
50
|
}
|
|
44
51
|
}
|
|
45
52
|
useLayoutEffect(() => {
|
|
@@ -56,7 +63,7 @@ export const ScrollView = forwardRef(({ children, style, contentWidth, contentHe
|
|
|
56
63
|
else {
|
|
57
64
|
scrollXRef.current = newPos;
|
|
58
65
|
}
|
|
59
|
-
|
|
66
|
+
SetWindowPos(content, 0 /* gui.SetWindowPosHwnd.TOP */, -scrollXRef.current, -scrollYRef.current, 0, 0, 1 /* gui.SetWindowPosFlag.SWP_NOSIZE */ | 4 /* gui.SetWindowPosFlag.SWP_NOZORDER */);
|
|
60
67
|
gui.SetScrollInfo(sv, bar, { pos: newPos }, true);
|
|
61
68
|
}
|
|
62
69
|
function handleScroll(e, bar) {
|
|
@@ -119,7 +126,7 @@ export const ScrollView = forwardRef(({ children, style, contentWidth, contentHe
|
|
|
119
126
|
fn: (e) => {
|
|
120
127
|
if (e.msg === 132 /* gui.WmMsg.NCHITTEST */ ||
|
|
121
128
|
e.msg === 161 /* gui.WmMsg.NCLBUTTONDOWN */) {
|
|
122
|
-
return
|
|
129
|
+
return DefWindowProc(e.hwnd, e.msg, e.wParam, e.lParam);
|
|
123
130
|
}
|
|
124
131
|
if (e.msg === 277 /* gui.WmMsg.VSCROLL */) {
|
|
125
132
|
handleScroll(e, 1 /* gui.ScrollBar.VERT */);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { forwardRef, useRef, useEffect } from 'react';
|
|
3
3
|
import * as gui from 'gui';
|
|
4
|
+
import { DefWindowProc } from '../../windows/user32.js';
|
|
4
5
|
export const Slider = forwardRef(({ value, onChange, min = 0, max = 100, vertical = false, disabled, style }, ref) => {
|
|
5
6
|
const wrapperRef = useRef(null);
|
|
6
7
|
const sliderRef = useRef(null);
|
|
@@ -30,7 +31,7 @@ export const Slider = forwardRef(({ value, onChange, min = 0, max = 100, vertica
|
|
|
30
31
|
}, onEvent: (e) => {
|
|
31
32
|
if (e.msg === 132 /* gui.WmMsg.NCHITTEST */ ||
|
|
32
33
|
e.msg === 161 /* gui.WmMsg.NCLBUTTONDOWN */) {
|
|
33
|
-
return
|
|
34
|
+
return DefWindowProc(e.hwnd, e.msg, e.wParam, e.lParam);
|
|
34
35
|
}
|
|
35
36
|
if (e.msg === 276 /* gui.WmMsg.HSCROLL */ || e.msg === 277 /* gui.WmMsg.VSCROLL */) {
|
|
36
37
|
const h = sliderRef.current;
|
|
@@ -1,25 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { forwardRef, useRef, useEffect, useState } from 'react';
|
|
3
3
|
import * as gui from 'gui';
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
4
|
+
import { WCHAR } from '../../ffi/bind.js';
|
|
5
|
+
import { TCITEMW } from '../../windows/structs.js';
|
|
6
6
|
import { nmCode } from '../nmhdr.js';
|
|
7
|
-
const TCITEMW = struct({
|
|
8
|
-
mask: 'u32',
|
|
9
|
-
dwState: 'u32',
|
|
10
|
-
dwStateMask: 'u32',
|
|
11
|
-
pszText: 'ptr',
|
|
12
|
-
cchTextMax: 'i32',
|
|
13
|
-
iImage: 'i32',
|
|
14
|
-
lParam: 'ptr',
|
|
15
|
-
});
|
|
16
|
-
function textToUtf16(s) {
|
|
17
|
-
const buf = new ArrayBuffer((s.length + 1) * 2);
|
|
18
|
-
const dv = new DataView(buf);
|
|
19
|
-
for (let i = 0; i < s.length; i++)
|
|
20
|
-
dv.setUint16(i * 2, s.charCodeAt(i), true);
|
|
21
|
-
return buf;
|
|
22
|
-
}
|
|
23
7
|
export const Tab = forwardRef(({ tabs, selectedIndex: controlledIndex, defaultSelectedIndex = 0, onChange, style }, ref) => {
|
|
24
8
|
const [internalIndex, setInternalIndex] = useState(defaultSelectedIndex);
|
|
25
9
|
const isControlled = controlledIndex !== undefined;
|
|
@@ -31,17 +15,17 @@ export const Tab = forwardRef(({ tabs, selectedIndex: controlledIndex, defaultSe
|
|
|
31
15
|
return;
|
|
32
16
|
gui.SendMessage(h, 4873 /* gui.TcMsg.DELETEALLITEMS */, 0, 0);
|
|
33
17
|
for (let i = 0; i < tabs.length; i++) {
|
|
34
|
-
const titleBuf =
|
|
35
|
-
const tci = TCITEMW.
|
|
18
|
+
const titleBuf = WCHAR.encode(tabs[i].title);
|
|
19
|
+
const tci = TCITEMW.encode({
|
|
36
20
|
mask: 1 /* gui.TcItemFlag.TEXT */,
|
|
37
21
|
dwState: 0,
|
|
38
22
|
dwStateMask: 0,
|
|
39
|
-
pszText:
|
|
23
|
+
pszText: titleBuf.ptr,
|
|
40
24
|
cchTextMax: tabs[i].title.length + 1,
|
|
41
25
|
iImage: 0,
|
|
42
26
|
lParam: 0,
|
|
43
27
|
});
|
|
44
|
-
const tciPtr =
|
|
28
|
+
const tciPtr = tci.ptr;
|
|
45
29
|
gui.SendMessage(h, 4926 /* gui.TcMsg.INSERTITEMW */, i, tciPtr);
|
|
46
30
|
}
|
|
47
31
|
gui.SendMessage(h, 4876 /* gui.TcMsg.SETCURSEL */, sel, 0);
|