quickwin 1.0.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 +197 -0
- package/README.md +196 -0
- package/examples/PdfCanvas.js +67 -0
- package/examples/PdfCanvas.tsx +83 -0
- package/examples/pdf_preview2.js +431 -0
- package/examples/pdf_preview2.ts +375 -0
- package/examples/pdf_viewer.js +17623 -0
- package/examples/pdf_viewer.tsx +279 -0
- package/examples/test_gallery.js +17452 -0
- package/examples/test_gallery.tsx +285 -0
- package/examples/test_react_lazy.js +17336 -0
- package/examples/test_react_lazy.tsx +63 -0
- package/examples/test_react_listbox.js +17339 -0
- package/examples/test_react_listbox.tsx +71 -0
- package/examples/test_root_window.js +17313 -0
- package/examples/test_root_window.tsx +41 -0
- package/examples/test_tab.js +17339 -0
- package/examples/test_tab.tsx +75 -0
- package/examples/tray_demo.js +79 -0
- package/examples/tray_demo.tsx +81 -0
- package/lib/assert.js +8 -0
- package/lib/assert.ts +7 -0
- package/lib/cache-utils.js +43 -0
- package/lib/cache-utils.ts +40 -0
- package/lib/dumpRects.js +25 -0
- package/lib/dumpRects.ts +25 -0
- package/lib/fetch.js +490 -0
- package/lib/fetch.ts +502 -0
- package/lib/http-common.js +342 -0
- package/lib/http-common.ts +399 -0
- package/lib/http-server.js +416 -0
- package/lib/http-server.ts +424 -0
- package/lib/polyfill.js +156 -0
- package/lib/polyfill.ts +171 -0
- package/lib/react-qw/components/Button.js +8 -0
- package/lib/react-qw/components/CheckBox.js +31 -0
- package/lib/react-qw/components/ComboBox.js +42 -0
- package/lib/react-qw/components/DateTimePicker.js +96 -0
- package/lib/react-qw/components/Input.js +50 -0
- package/lib/react-qw/components/Link.js +43 -0
- package/lib/react-qw/components/ListBox.js +48 -0
- package/lib/react-qw/components/ListView.js +328 -0
- package/lib/react-qw/components/PathPicker.js +136 -0
- package/lib/react-qw/components/ProgressBar.js +22 -0
- package/lib/react-qw/components/RadioButton.js +31 -0
- package/lib/react-qw/components/ScrollView.js +139 -0
- package/lib/react-qw/components/Slider.js +45 -0
- package/lib/react-qw/components/Tab.js +61 -0
- package/lib/react-qw/components/Tooltip.js +55 -0
- package/lib/react-qw/components/TreeView.js +130 -0
- package/lib/react-qw/components/index.js +16 -0
- package/lib/react-qw/components/index.ts +17 -0
- package/lib/react-qw/index.js +73 -0
- package/lib/react-qw/index.ts +90 -0
- package/lib/react-qw/jsx.d.ts +34 -0
- package/lib/react-qw/layout.js +82 -0
- package/lib/react-qw/layout.ts +111 -0
- package/lib/react-qw/props.js +27 -0
- package/lib/react-qw/props.ts +36 -0
- package/lib/react-qw/reconciler.js +333 -0
- package/lib/react-qw/reconciler.ts +373 -0
- package/lib/stream.js +2 -0
- package/lib/stream.ts +22 -0
- package/lib/text-codec.js +197 -0
- package/lib/text-codec.ts +171 -0
- package/lib/text-measure.js +52 -0
- package/lib/text-measure.ts +54 -0
- package/lib/url.js +278 -0
- package/lib/url.ts +201 -0
- package/lib/vendor/web-streams/ponyfill.d.mts +829 -0
- package/lib/vendor/web-streams/ponyfill.mjs +8 -0
- package/lib/websocket.js +541 -0
- package/lib/websocket.ts +555 -0
- package/package.json +44 -0
- package/quickwin.d.ts +594 -0
- package/quickwin_const.d.ts +900 -0
- package/qwin-nowasm.exe +0 -0
- package/qwin.exe +0 -0
- package/test/add.wasm +0 -0
- package/test/complex.wasm +0 -0
- package/test/complex_imports.wasm +0 -0
- package/test/debug/http_server_e2e.js +31 -0
- package/test/debug/minwasm.js +52 -0
- package/test/debug/mupdf_debug.js +104 -0
- package/test/debug/wasm.js +122 -0
- package/test/debug/wasm_export_debug.js +64 -0
- package/test/debug/wasm_names.js +46 -0
- package/test/global_imports.wasm +0 -0
- package/test/import_func.wasm +0 -0
- package/test/imports.wasm +0 -0
- package/test/run.js +91 -0
- package/test/run.ts +96 -0
- package/test/sjlj.wasm +0 -0
- package/test/test_basic.js +7 -0
- package/test/test_basic.ts +9 -0
- package/test/test_basic_fetch.js +34 -0
- package/test/test_basic_fetch.ts +33 -0
- package/test/test_brotli.js +48 -0
- package/test/test_brotli.ts +52 -0
- package/test/test_fetch_cache.js +169 -0
- package/test/test_fetch_cache.ts +187 -0
- package/test/test_ffi.js +157 -0
- package/test/test_ffi.ts +174 -0
- package/test/test_frame_encoding.js +128 -0
- package/test/test_frame_encoding.ts +132 -0
- package/test/test_helper.js +84 -0
- package/test/test_helper.ts +80 -0
- package/test/test_http_import.js +78 -0
- package/test/test_http_import.ts +74 -0
- package/test/test_http_server.js +142 -0
- package/test/test_http_server.ts +136 -0
- package/test/test_import_dir.js +4 -0
- package/test/test_import_dir.ts +5 -0
- package/test/test_ipv6.js +134 -0
- package/test/test_ipv6.ts +129 -0
- package/test/test_jsx.js +1314 -0
- package/test/test_meta_url.js +3 -0
- package/test/test_meta_url.ts +4 -0
- package/test/test_mupdf_render.js +69 -0
- package/test/test_mupdf_render.ts +74 -0
- package/test/test_mupdf_twice.js +77 -0
- package/test/test_mupdf_twice.ts +81 -0
- package/test/test_mupdf_wasm.js +33 -0
- package/test/test_mupdf_wasm.ts +30 -0
- package/test/test_net_event.js +57 -0
- package/test/test_net_event.ts +55 -0
- package/test/test_net_fetch.js +259 -0
- package/test/test_net_fetch.ts +238 -0
- package/test/test_net_websocket.js +180 -0
- package/test/test_net_websocket.ts +164 -0
- package/test/test_polyfill.js +144 -0
- package/test/test_polyfill.ts +159 -0
- package/test/test_react_complex.js +17440 -0
- package/test/test_react_counter.js +17331 -0
- package/test/test_react_flex.js +17643 -0
- package/test/test_react_hidden.js +17336 -0
- package/test/test_react_listview_link.js +17371 -0
- package/test/test_react_render.js +17326 -0
- package/test/test_url.js +173 -0
- package/test/test_url.ts +183 -0
- package/test/test_wasm_basic.js +85 -0
- package/test/test_wasm_basic.ts +73 -0
- package/test/test_wasm_import_global.js +41 -0
- package/test/test_wasm_import_global.ts +45 -0
- package/test/test_wasm_sjlj.js +145 -0
- package/test/test_wasm_sjlj.ts +125 -0
- package/test/test_wasm_types.js +97 -0
- package/test/test_wasm_types.ts +124 -0
- package/test/test_worker.js +21 -0
- package/test/test_worker.ts +24 -0
- package/test/test_worker_fetch.js +26 -0
- package/test/test_worker_fetch.ts +27 -0
- package/test/test_worker_net.js +51 -0
- package/test/test_worker_net.ts +54 -0
- package/test/test_worker_wasm_concurrent.js +44 -0
- package/test/test_worker_wasm_concurrent.ts +45 -0
- package/test/types.wasm +0 -0
- package/test/worker_fetch.js +13 -0
- package/test/worker_fetch.ts +13 -0
- package/test/worker_helper.js +1 -0
- package/test/worker_helper.ts +1 -0
- package/test/worker_http.js +18 -0
- package/test/worker_http.ts +20 -0
- package/test/worker_import_lib.js +12 -0
- package/test/worker_import_lib.ts +13 -0
- package/test/worker_module.js +16 -0
- package/test/worker_module.ts +17 -0
- package/test/worker_wasm_concurrent.js +22 -0
- package/test/worker_wasm_concurrent.ts +22 -0
- package/tsconfig.json +27 -0
- package/vendor/mupdf-wasm/mupdf-wasm.d.ts +571 -0
- package/vendor/mupdf-wasm/mupdf-wasm.js +2749 -0
- package/vendor/mupdf-wasm/mupdf-wasm.wasm +0 -0
- package/vendor/mupdf-wasm/mupdf.d.ts +939 -0
- package/vendor/mupdf-wasm/mupdf.js +3317 -0
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef, useRef, useEffect } from 'react';
|
|
3
|
+
import * as gui from 'gui';
|
|
4
|
+
import * as ffi from 'ffi';
|
|
5
|
+
import * as win from 'win';
|
|
6
|
+
export function makeColorBlock(size, bgra) {
|
|
7
|
+
const n = size * size;
|
|
8
|
+
const buf = new ArrayBuffer(n * 4);
|
|
9
|
+
const b = new Uint8Array(buf);
|
|
10
|
+
for (let i = 0; i < n; i++) {
|
|
11
|
+
b[i * 4] = bgra & 0xFF;
|
|
12
|
+
b[i * 4 + 1] = (bgra >> 8) & 0xFF;
|
|
13
|
+
b[i * 4 + 2] = (bgra >> 16) & 0xFF;
|
|
14
|
+
b[i * 4 + 3] = 0xFF;
|
|
15
|
+
}
|
|
16
|
+
return buf;
|
|
17
|
+
}
|
|
18
|
+
function textToUtf16(s) {
|
|
19
|
+
const buf = new ArrayBuffer((s.length + 1) * 2);
|
|
20
|
+
const dv = new DataView(buf);
|
|
21
|
+
for (let i = 0; i < s.length; i++)
|
|
22
|
+
dv.setUint16(i * 2, s.charCodeAt(i), true);
|
|
23
|
+
return buf;
|
|
24
|
+
}
|
|
25
|
+
function readI32(ptr, offset) {
|
|
26
|
+
return ffi.readByte(ptr + offset) | (ffi.readByte(ptr + offset + 1) << 8) |
|
|
27
|
+
(ffi.readByte(ptr + offset + 2) << 16) | (ffi.readByte(ptr + offset + 3) << 24);
|
|
28
|
+
}
|
|
29
|
+
function readU32(ptr, offset) {
|
|
30
|
+
return ffi.readByte(ptr + offset) | (ffi.readByte(ptr + offset + 1) << 8) |
|
|
31
|
+
(ffi.readByte(ptr + offset + 2) << 16) | (ffi.readByte(ptr + offset + 3) << 24) >>> 0;
|
|
32
|
+
}
|
|
33
|
+
function readU64(ptr, offset) {
|
|
34
|
+
const lo = readU32(ptr, offset);
|
|
35
|
+
const hi = readU32(ptr, offset + 4);
|
|
36
|
+
return lo + hi * 0x100000000;
|
|
37
|
+
}
|
|
38
|
+
function writeU32(ptr, offset, v) {
|
|
39
|
+
ffi.writeByte(ptr + offset, v & 0xFF);
|
|
40
|
+
ffi.writeByte(ptr + offset + 1, (v >> 8) & 0xFF);
|
|
41
|
+
ffi.writeByte(ptr + offset + 2, (v >> 16) & 0xFF);
|
|
42
|
+
ffi.writeByte(ptr + offset + 3, (v >> 24) & 0xFF);
|
|
43
|
+
}
|
|
44
|
+
function bufPtr(buf) {
|
|
45
|
+
return ffi.bufferPtr(buf);
|
|
46
|
+
}
|
|
47
|
+
const LV_WS = 268435456 /* gui.WindowStyle.VISIBLE */ | 8388608 /* gui.WindowStyle.BORDER */ | 2097152 /* gui.WindowStyle.VSCROLL */ | 1048576 /* gui.WindowStyle.HSCROLL */
|
|
48
|
+
| 1 /* gui.ListViewStyle.REPORT */ | 4 /* gui.ListViewStyle.SINGLESEL */;
|
|
49
|
+
// x64 结构体偏移(NMHDR 为 24 字节)
|
|
50
|
+
const CD_STAGE = 24; // NMCUSTOMDRAW.dwDrawStage
|
|
51
|
+
const CD_HDC = 32; // NMCUSTOMDRAW.hdc
|
|
52
|
+
const CD_ITEM = 56; // NMCUSTOMDRAW.dwItemSpec(行索引)
|
|
53
|
+
const CD_CLRTEXT = 80; // NMLVCUSTOMDRAW.clrText
|
|
54
|
+
const CD_CLRTEXTBK = 84; // NMLVCUSTOMDRAW.clrTextBk
|
|
55
|
+
const CD_SUBITEM = 88; // NMLVCUSTOMDRAW.iSubItem
|
|
56
|
+
const NMIA_ITEM = 24; // NMITEMACTIVATE.iItem
|
|
57
|
+
const NMIA_SUBITEM = 28; // NMITEMACTIVATE.iSubItem
|
|
58
|
+
const NM_CODE = 16; // NMHDR.code
|
|
59
|
+
const fontCache = new Map();
|
|
60
|
+
let gdi32 = null;
|
|
61
|
+
let createFontIndirectW = null;
|
|
62
|
+
let selectObjectFn = null;
|
|
63
|
+
let getObjectW = null;
|
|
64
|
+
function ensureGdi() {
|
|
65
|
+
if (gdi32 !== null)
|
|
66
|
+
return;
|
|
67
|
+
gdi32 = win.LoadLibrary('gdi32.dll');
|
|
68
|
+
if (!gdi32)
|
|
69
|
+
return;
|
|
70
|
+
createFontIndirectW = win.GetProcAddress(gdi32, 'CreateFontIndirectW');
|
|
71
|
+
selectObjectFn = win.GetProcAddress(gdi32, 'SelectObject');
|
|
72
|
+
getObjectW = win.GetProcAddress(gdi32, 'GetObjectW');
|
|
73
|
+
}
|
|
74
|
+
let user32 = null;
|
|
75
|
+
let loadCursorW = null;
|
|
76
|
+
let setCursorFn = null;
|
|
77
|
+
let screenToClient = null;
|
|
78
|
+
function ensureUser32() {
|
|
79
|
+
if (user32 !== null)
|
|
80
|
+
return;
|
|
81
|
+
user32 = win.LoadLibrary('user32.dll');
|
|
82
|
+
if (!user32)
|
|
83
|
+
return;
|
|
84
|
+
loadCursorW = win.GetProcAddress(user32, 'LoadCursorW');
|
|
85
|
+
setCursorFn = win.GetProcAddress(user32, 'SetCursor');
|
|
86
|
+
screenToClient = win.GetProcAddress(user32, 'ScreenToClient');
|
|
87
|
+
}
|
|
88
|
+
function getCellFont(hwnd, style) {
|
|
89
|
+
const key = (style.bold ? 'b' : '') + (style.italic ? 'i' : '') + (style.underline ? 'u' : '');
|
|
90
|
+
if (key === '')
|
|
91
|
+
return null;
|
|
92
|
+
const cached = fontCache.get(key);
|
|
93
|
+
if (cached !== undefined)
|
|
94
|
+
return cached === 0 ? null : cached;
|
|
95
|
+
ensureGdi();
|
|
96
|
+
if (!createFontIndirectW || !getObjectW || !hwnd)
|
|
97
|
+
return null;
|
|
98
|
+
const lf = new ArrayBuffer(92);
|
|
99
|
+
const dv = new DataView(lf);
|
|
100
|
+
const cur = gui.SendMessage(hwnd, 49 /* gui.WmMsg.GETFONT */, 0, 0);
|
|
101
|
+
if (cur) {
|
|
102
|
+
const got = ffi.ffiCall(getObjectW, [ffi.FFI_TYPE_UINT64, ffi.FFI_TYPE_SINT32, ffi.FFI_TYPE_POINTER], [cur, 92, lf], ffi.FFI_TYPE_SINT32);
|
|
103
|
+
if (!got)
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
dv.setInt32(0, -13, true);
|
|
108
|
+
}
|
|
109
|
+
if (style.bold)
|
|
110
|
+
dv.setInt32(16, 700 /* gui.FontWeight.BOLD */, true);
|
|
111
|
+
if (style.italic)
|
|
112
|
+
dv.setUint8(20, 1);
|
|
113
|
+
if (style.underline)
|
|
114
|
+
dv.setUint8(21, 1);
|
|
115
|
+
const h = ffi.ffiCall(createFontIndirectW, [ffi.FFI_TYPE_POINTER], [lf], ffi.FFI_TYPE_UINT64);
|
|
116
|
+
fontCache.set(key, h === 0 ? 0 : h);
|
|
117
|
+
return h === 0 ? null : h;
|
|
118
|
+
}
|
|
119
|
+
function handleCustomDraw(lParam, columns, data, hwnd) {
|
|
120
|
+
const stage = readU32(lParam, CD_STAGE);
|
|
121
|
+
if (stage === 1 /* gui.CustomDrawStage.PREPAINT */)
|
|
122
|
+
return 32 /* gui.CustomDrawFlag.NOTIFYITEMDRAW */;
|
|
123
|
+
if (stage === 65537 /* gui.CustomDrawStage.ITEMPREPAINT */)
|
|
124
|
+
return 32 /* gui.CustomDrawFlag.NOTIFYSUBITEMDRAW */;
|
|
125
|
+
if (stage === 196609 /* gui.CustomDrawStage.SUBITEMPREPAINT */) {
|
|
126
|
+
const colIndex = readI32(lParam, CD_SUBITEM);
|
|
127
|
+
const row = readI32(lParam, CD_ITEM);
|
|
128
|
+
const style = resolveCellStyle(columns, data, row, colIndex);
|
|
129
|
+
if (!style)
|
|
130
|
+
return 0 /* gui.CustomDrawFlag.DODEFAULT */;
|
|
131
|
+
if (style.color !== undefined)
|
|
132
|
+
writeU32(lParam, CD_CLRTEXT, style.color);
|
|
133
|
+
if (style.background !== undefined)
|
|
134
|
+
writeU32(lParam, CD_CLRTEXTBK, style.background);
|
|
135
|
+
const hfont = getCellFont(hwnd, style);
|
|
136
|
+
if (hfont && selectObjectFn) {
|
|
137
|
+
const hdc = readU64(lParam, CD_HDC);
|
|
138
|
+
if (hdc) {
|
|
139
|
+
ffi.ffiCall(selectObjectFn, [ffi.FFI_TYPE_UINT64, ffi.FFI_TYPE_UINT64], [hdc, hfont], ffi.FFI_TYPE_UINT64);
|
|
140
|
+
return 2 /* gui.CustomDrawFlag.NEWFONT */;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return 0 /* gui.CustomDrawFlag.DODEFAULT */;
|
|
145
|
+
}
|
|
146
|
+
function alignToFmt(align) {
|
|
147
|
+
if (align === 'center')
|
|
148
|
+
return 2 /* gui.LvColumnFormat.CENTER */;
|
|
149
|
+
if (align === 'right')
|
|
150
|
+
return 1 /* gui.LvColumnFormat.RIGHT */;
|
|
151
|
+
return 0 /* gui.LvColumnFormat.LEFT */;
|
|
152
|
+
}
|
|
153
|
+
function cellText(record, col, index) {
|
|
154
|
+
if (col.render)
|
|
155
|
+
return col.render(record, index);
|
|
156
|
+
if (col.dataIndex === undefined)
|
|
157
|
+
return '';
|
|
158
|
+
const v = record[col.dataIndex];
|
|
159
|
+
return v == null ? '' : String(v);
|
|
160
|
+
}
|
|
161
|
+
function resolveCellStyle(columns, data, row, colIndex) {
|
|
162
|
+
const col = columns[colIndex];
|
|
163
|
+
if (!col || !col.cellStyle)
|
|
164
|
+
return undefined;
|
|
165
|
+
const record = data[row];
|
|
166
|
+
if (record === undefined)
|
|
167
|
+
return undefined;
|
|
168
|
+
const style = typeof col.cellStyle === 'function' ? col.cellStyle(record, row) : col.cellStyle;
|
|
169
|
+
return style || undefined;
|
|
170
|
+
}
|
|
171
|
+
function makeLVItem(i, sub, text, image) {
|
|
172
|
+
const b = new ArrayBuffer(84);
|
|
173
|
+
const dv = new DataView(b);
|
|
174
|
+
dv.setInt32(4, i, true);
|
|
175
|
+
dv.setInt32(8, sub, true);
|
|
176
|
+
let mask = 1 /* LvItemFlag.TEXT */;
|
|
177
|
+
if (image !== undefined) {
|
|
178
|
+
mask |= 2 /* LvItemFlag.IMAGE */;
|
|
179
|
+
dv.setInt32(36, image, true);
|
|
180
|
+
}
|
|
181
|
+
dv.setUint32(0, mask, true);
|
|
182
|
+
const textBuf = textToUtf16(text);
|
|
183
|
+
dv.setBigUint64(24, BigInt(bufPtr(textBuf)), true);
|
|
184
|
+
b.__textBuf = textBuf;
|
|
185
|
+
return b;
|
|
186
|
+
}
|
|
187
|
+
const ListView = forwardRef(function ListViewInner({ columns, data, style, icons, iconSize, getIcon }, ref) {
|
|
188
|
+
const lvRef = useRef(null);
|
|
189
|
+
const iconListRef = useRef(0);
|
|
190
|
+
useEffect(() => {
|
|
191
|
+
const h = lvRef.current;
|
|
192
|
+
if (!h)
|
|
193
|
+
return;
|
|
194
|
+
const il = iconListRef.current;
|
|
195
|
+
iconListRef.current = 0;
|
|
196
|
+
if (il) {
|
|
197
|
+
gui.SendMessage(h, 4099 /* gui.LvMsg.SETIMAGELIST */, 1 /* gui.LvImageList.SMALL */, 0);
|
|
198
|
+
gui.ImageListDestroy(il);
|
|
199
|
+
}
|
|
200
|
+
if (!icons || icons.length === 0)
|
|
201
|
+
return;
|
|
202
|
+
const size = iconSize ?? 32;
|
|
203
|
+
const img = gui.ImageListCreate(size, size, 32 /* gui.ImageListFlag.COLOR32 */, icons.length, 1);
|
|
204
|
+
if (!img)
|
|
205
|
+
return;
|
|
206
|
+
for (const p of icons) {
|
|
207
|
+
const hbm = gui.CreateBitmapFromPixels(size, size, p);
|
|
208
|
+
if (hbm) {
|
|
209
|
+
gui.ImageListAdd(img, hbm);
|
|
210
|
+
gui.DeleteObject(hbm);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
gui.SendMessage(h, 4099 /* gui.LvMsg.SETIMAGELIST */, 1 /* gui.LvImageList.SMALL */, img);
|
|
214
|
+
iconListRef.current = img;
|
|
215
|
+
}, [icons, iconSize]);
|
|
216
|
+
useEffect(() => {
|
|
217
|
+
const h = lvRef.current;
|
|
218
|
+
if (!h)
|
|
219
|
+
return;
|
|
220
|
+
// 从后往前删旧列
|
|
221
|
+
const hdr = gui.SendMessage(h, 4127 /* gui.LvMsg.GETHEADER */, 0, 0);
|
|
222
|
+
if (hdr) {
|
|
223
|
+
let n = gui.SendMessage(hdr, 4608 /* gui.HdmMsg.GETITEMCOUNT */, 0, 0);
|
|
224
|
+
for (let k = n - 1; k >= 0; k--)
|
|
225
|
+
gui.SendMessage(h, 4124 /* gui.LvMsg.DELETECOLUMN */, k, 0);
|
|
226
|
+
}
|
|
227
|
+
gui.SendMessage(h, 4150 /* gui.LvMsg.SETEXTENDEDLISTVIEWSTYLE */, 0, 32 /* gui.LvExStyle.FULLROWSELECT */ | 1 /* gui.LvExStyle.GRIDLINES */ | 65536 /* gui.LvExStyle.DOUBLEBUFFER */);
|
|
228
|
+
const n = columns.length;
|
|
229
|
+
for (let j = 0; j < n; j++) {
|
|
230
|
+
const titleBuf = textToUtf16(columns[j].name);
|
|
231
|
+
const lvc = new ArrayBuffer(52);
|
|
232
|
+
const dv = new DataView(lvc);
|
|
233
|
+
dv.setUint32(0, 4 /* LvColumnMask.TEXT */ | 2 /* LvColumnMask.WIDTH */ | 1 /* LvColumnMask.FORMAT */, true);
|
|
234
|
+
dv.setInt32(4, alignToFmt(columns[j].align), true);
|
|
235
|
+
dv.setInt32(8, columns[j].width ?? 100, true);
|
|
236
|
+
dv.setBigUint64(16, BigInt(bufPtr(titleBuf)), true);
|
|
237
|
+
dv.setInt32(28, j, true);
|
|
238
|
+
lvc.__titleBuf = titleBuf;
|
|
239
|
+
gui.SendMessage(h, 4193 /* gui.LvMsg.INSERTCOLUMNW */, j, bufPtr(lvc));
|
|
240
|
+
}
|
|
241
|
+
}, [columns]);
|
|
242
|
+
useEffect(() => {
|
|
243
|
+
const h = lvRef.current;
|
|
244
|
+
if (!h)
|
|
245
|
+
return;
|
|
246
|
+
gui.SendMessage(h, 4105 /* gui.LvMsg.DELETEALLITEMS */, 0, 0);
|
|
247
|
+
const nCols = columns.length;
|
|
248
|
+
for (let i = 0; i < data.length; i++) {
|
|
249
|
+
const record = data[i];
|
|
250
|
+
const img = getIcon ? getIcon(record, i) : undefined;
|
|
251
|
+
const itemBuf = makeLVItem(i, 0, cellText(record, columns[0], i), img);
|
|
252
|
+
gui.SendMessage(h, 4173 /* gui.LvMsg.INSERTITEMW */, 0, bufPtr(itemBuf));
|
|
253
|
+
for (let j = 1; j < nCols; j++) {
|
|
254
|
+
const subBuf = makeLVItem(i, j, cellText(record, columns[j], i));
|
|
255
|
+
gui.SendMessage(h, 4172 /* gui.LvMsg.SETITEMW */, 0, bufPtr(subBuf));
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
for (let j = 0; j < columns.length; j++) {
|
|
259
|
+
if (columns[j].width == null) {
|
|
260
|
+
gui.SendMessage(h, 4126 /* gui.LvMsg.SETCOLUMNWIDTH */, j, -2 /* gui.LvColumnWidthCmd.AUTOSIZE_USEHEADER */);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}, [data, columns, getIcon]);
|
|
264
|
+
return (_jsx("w", { type: "STATIC", ws: 268435456 /* gui.WindowStyle.VISIBLE */ | 33554432 /* gui.WindowStyle.CLIPCHILDREN */, style: { ...style, flexDirection: 'column', alignItems: 'stretch' }, ref: ref, onEvent: (e) => {
|
|
265
|
+
if (e.msg === 78 /* gui.WmMsg.NOTIFY */) {
|
|
266
|
+
const code = readI32(e.lParam, NM_CODE);
|
|
267
|
+
if (code === -12 /* gui.LvNotifyCode.CUSTOMDRAW */) {
|
|
268
|
+
return handleCustomDraw(e.lParam, columns, data, lvRef.current);
|
|
269
|
+
}
|
|
270
|
+
if (code === -100 /* gui.LvNotifyCode.ITEMCHANGING */) {
|
|
271
|
+
const uNewState = readU32(e.lParam, 32);
|
|
272
|
+
const uOldState = readU32(e.lParam, 36);
|
|
273
|
+
if ((uNewState & 2 /* LvItemState.SELECTED */) !== (uOldState & 2 /* LvItemState.SELECTED */))
|
|
274
|
+
return 1;
|
|
275
|
+
}
|
|
276
|
+
if (code === -2 /* gui.LvNotifyCode.CLICK */) {
|
|
277
|
+
const iItem = readI32(e.lParam, NMIA_ITEM);
|
|
278
|
+
const iSubItem = readI32(e.lParam, NMIA_SUBITEM);
|
|
279
|
+
const col = columns[iSubItem];
|
|
280
|
+
const record = data[iItem];
|
|
281
|
+
if (col?.onCellClick && record !== undefined)
|
|
282
|
+
col.onCellClick(record, iItem);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return;
|
|
286
|
+
}, children: _jsx("w", { type: "SysListView32", ws: LV_WS, style: { flexGrow: 1 }, ref: (h) => {
|
|
287
|
+
lvRef.current = h;
|
|
288
|
+
}, onEvent: (e) => {
|
|
289
|
+
if (e.msg !== 32 /* gui.WmMsg.SETCURSOR */)
|
|
290
|
+
return;
|
|
291
|
+
if ((e.lParam & 0xFFFF) !== 1 /* gui.HitTest.CLIENT */)
|
|
292
|
+
return;
|
|
293
|
+
const h = lvRef.current;
|
|
294
|
+
if (!h)
|
|
295
|
+
return;
|
|
296
|
+
ensureUser32();
|
|
297
|
+
if (!loadCursorW || !setCursorFn || !screenToClient)
|
|
298
|
+
return;
|
|
299
|
+
const sp = gui.GetCursorPos();
|
|
300
|
+
if (!sp)
|
|
301
|
+
return;
|
|
302
|
+
const sbuf = new ArrayBuffer(8);
|
|
303
|
+
const sdv = new DataView(sbuf);
|
|
304
|
+
sdv.setInt32(0, sp[0], true);
|
|
305
|
+
sdv.setInt32(4, sp[1], true);
|
|
306
|
+
ffi.ffiCall(screenToClient, [ffi.FFI_TYPE_UINT64, ffi.FFI_TYPE_POINTER], [h, sbuf], ffi.FFI_TYPE_SINT32);
|
|
307
|
+
const lvhi = new ArrayBuffer(24);
|
|
308
|
+
const lvd = new DataView(lvhi);
|
|
309
|
+
lvd.setInt32(0, sdv.getInt32(0, true), true);
|
|
310
|
+
lvd.setInt32(4, sdv.getInt32(4, true), true);
|
|
311
|
+
lvd.setInt32(12, -1, true);
|
|
312
|
+
lvd.setInt32(16, -1, true);
|
|
313
|
+
const lvhiPtr = bufPtr(lvhi);
|
|
314
|
+
gui.SendMessage(h, 4153 /* gui.LvMsg.SUBITEMHITTEST */, 0, lvhiPtr);
|
|
315
|
+
const iItem = readI32(lvhiPtr, 12);
|
|
316
|
+
const iSubItem = readI32(lvhiPtr, 16);
|
|
317
|
+
const style = resolveCellStyle(columns, data, iItem, iSubItem);
|
|
318
|
+
if (!style || style.cursor === undefined)
|
|
319
|
+
return;
|
|
320
|
+
const hc = ffi.ffiCall(loadCursorW, [ffi.FFI_TYPE_UINT64, ffi.FFI_TYPE_UINT64], [0, style.cursor], ffi.FFI_TYPE_UINT64);
|
|
321
|
+
if (hc) {
|
|
322
|
+
ffi.ffiCall(setCursorFn, [ffi.FFI_TYPE_UINT64], [hc], ffi.FFI_TYPE_UINT64);
|
|
323
|
+
return 1;
|
|
324
|
+
}
|
|
325
|
+
return;
|
|
326
|
+
} }) }));
|
|
327
|
+
});
|
|
328
|
+
export { ListView };
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef, useState, useRef } from 'react';
|
|
3
|
+
import * as ffi from 'ffi';
|
|
4
|
+
import * as win from 'win';
|
|
5
|
+
const FFI_PTR = ffi.FFI_TYPE_POINTER;
|
|
6
|
+
const FFI_U32 = ffi.FFI_TYPE_UINT32;
|
|
7
|
+
let _GetOpenFileNameW = 0;
|
|
8
|
+
let _SHBrowseForFolderW = 0;
|
|
9
|
+
let _SHGetPathFromIDListW = 0;
|
|
10
|
+
let _CoTaskMemFree = 0;
|
|
11
|
+
function ensureDlls() {
|
|
12
|
+
if (_GetOpenFileNameW)
|
|
13
|
+
return true;
|
|
14
|
+
const comdlg32 = win.LoadLibrary('comdlg32.dll');
|
|
15
|
+
const shell32 = win.LoadLibrary('shell32.dll');
|
|
16
|
+
const ole32 = win.LoadLibrary('ole32.dll');
|
|
17
|
+
if (!comdlg32 || !shell32 || !ole32)
|
|
18
|
+
return false;
|
|
19
|
+
const load = (lib, name) => win.GetProcAddress(lib, name) || 0;
|
|
20
|
+
_GetOpenFileNameW = load(comdlg32, 'GetOpenFileNameW');
|
|
21
|
+
_SHBrowseForFolderW = load(shell32, 'SHBrowseForFolderW');
|
|
22
|
+
_SHGetPathFromIDListW = load(shell32, 'SHGetPathFromIDListW');
|
|
23
|
+
_CoTaskMemFree = load(ole32, 'CoTaskMemFree');
|
|
24
|
+
return !!_GetOpenFileNameW && !!_SHBrowseForFolderW && !!_SHGetPathFromIDListW && !!_CoTaskMemFree;
|
|
25
|
+
}
|
|
26
|
+
function strToWide(s) {
|
|
27
|
+
return new TextEncoder('utf-16le').encode(s + '\0').buffer;
|
|
28
|
+
}
|
|
29
|
+
const _decoder = new TextDecoder('utf-16le');
|
|
30
|
+
function wideToStr(buf, offset = 0) {
|
|
31
|
+
const str = _decoder.decode(new Uint8Array(buf, offset));
|
|
32
|
+
const nullIdx = str.indexOf('\0');
|
|
33
|
+
return nullIdx >= 0 ? str.substring(0, nullIdx) : str;
|
|
34
|
+
}
|
|
35
|
+
function setPtr(dv, off, ptr) {
|
|
36
|
+
dv.setUint32(off, ptr & 0xFFFFFFFF, true);
|
|
37
|
+
dv.setUint32(off + 4, Math.floor(ptr / 0x100000000), true);
|
|
38
|
+
}
|
|
39
|
+
function openFileDialog(owner, filter, title, multiple) {
|
|
40
|
+
if (!ensureDlls())
|
|
41
|
+
return null;
|
|
42
|
+
const structBuf = new ArrayBuffer(152);
|
|
43
|
+
const sv = new DataView(structBuf);
|
|
44
|
+
const fileBuf = new ArrayBuffer(260 * 2);
|
|
45
|
+
const filterWide = strToWide(filter);
|
|
46
|
+
const titleWide = title ? strToWide(title) : null;
|
|
47
|
+
sv.setUint32(0, 152, true);
|
|
48
|
+
setPtr(sv, 8, owner);
|
|
49
|
+
setPtr(sv, 24, ffi.bufferPtr(filterWide));
|
|
50
|
+
setPtr(sv, 48, ffi.bufferPtr(fileBuf));
|
|
51
|
+
sv.setUint32(56, 260, true);
|
|
52
|
+
let flags = 0x1000 | 0x0800 | 0x0008;
|
|
53
|
+
if (multiple)
|
|
54
|
+
flags |= 0x0200;
|
|
55
|
+
flags |= 0x80000;
|
|
56
|
+
sv.setUint32(96, flags, true);
|
|
57
|
+
if (titleWide)
|
|
58
|
+
setPtr(sv, 88, ffi.bufferPtr(titleWide));
|
|
59
|
+
const ret = ffi.ffiCall(_GetOpenFileNameW, [FFI_PTR], [structBuf], FFI_U32);
|
|
60
|
+
if (!ret)
|
|
61
|
+
return null;
|
|
62
|
+
if (!multiple) {
|
|
63
|
+
return wideToStr(fileBuf);
|
|
64
|
+
}
|
|
65
|
+
const dir = wideToStr(fileBuf);
|
|
66
|
+
let pos = (dir.length + 1) * 2;
|
|
67
|
+
const files = [];
|
|
68
|
+
while (pos < fileBuf.byteLength) {
|
|
69
|
+
const f = wideToStr(fileBuf, pos);
|
|
70
|
+
if (f.length === 0)
|
|
71
|
+
break;
|
|
72
|
+
files.push(dir + '\\' + f);
|
|
73
|
+
pos += (f.length + 1) * 2;
|
|
74
|
+
}
|
|
75
|
+
if (files.length === 0)
|
|
76
|
+
return [dir];
|
|
77
|
+
return files;
|
|
78
|
+
}
|
|
79
|
+
function openFolderDialog(owner, title) {
|
|
80
|
+
if (!ensureDlls())
|
|
81
|
+
return null;
|
|
82
|
+
const structBuf = new ArrayBuffer(64);
|
|
83
|
+
const sv = new DataView(structBuf);
|
|
84
|
+
const titleWide = title ? strToWide(title) : null;
|
|
85
|
+
setPtr(sv, 0, owner);
|
|
86
|
+
if (titleWide)
|
|
87
|
+
setPtr(sv, 24, ffi.bufferPtr(titleWide));
|
|
88
|
+
sv.setUint32(32, 0x00000041, true);
|
|
89
|
+
const pidl = ffi.ffiCall(_SHBrowseForFolderW, [FFI_PTR], [structBuf], FFI_PTR);
|
|
90
|
+
if (!pidl)
|
|
91
|
+
return null;
|
|
92
|
+
const pathBuf = new ArrayBuffer(260 * 2);
|
|
93
|
+
const ok = ffi.ffiCall(_SHGetPathFromIDListW, [ffi.FFI_TYPE_UINT64, FFI_PTR], [pidl, pathBuf], FFI_U32);
|
|
94
|
+
ffi.ffiCall(_CoTaskMemFree, [ffi.FFI_TYPE_UINT64], [pidl], ffi.FFI_TYPE_VOID);
|
|
95
|
+
return ok ? wideToStr(pathBuf) : null;
|
|
96
|
+
}
|
|
97
|
+
export const PathPicker = forwardRef((props, ref) => {
|
|
98
|
+
const { type = 'file', filter = 'All Files\0*.*\0\0', title, disabled, style } = props;
|
|
99
|
+
const multiple = props.multiple === true;
|
|
100
|
+
const controlledValue = props.value;
|
|
101
|
+
const defaultValue = props.defaultValue;
|
|
102
|
+
const onChange = props.onChange;
|
|
103
|
+
const [internalValue, setInternalValue] = useState(defaultValue ?? (multiple ? [] : ''));
|
|
104
|
+
const isControlled = controlledValue !== undefined;
|
|
105
|
+
const displayValue = isControlled ? controlledValue : internalValue;
|
|
106
|
+
const containerRef = useRef(null);
|
|
107
|
+
const displayText = Array.isArray(displayValue) ? displayValue.join('\n') : (displayValue ?? '');
|
|
108
|
+
const handleBrowse = () => {
|
|
109
|
+
const owner = containerRef.current;
|
|
110
|
+
if (!owner)
|
|
111
|
+
return;
|
|
112
|
+
let result;
|
|
113
|
+
if (type === 'folder') {
|
|
114
|
+
const r = openFolderDialog(owner, title);
|
|
115
|
+
result = r !== null ? (multiple ? [r] : r) : null;
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
result = openFileDialog(owner, filter, title, multiple);
|
|
119
|
+
}
|
|
120
|
+
if (result === null)
|
|
121
|
+
return;
|
|
122
|
+
if (!isControlled)
|
|
123
|
+
setInternalValue(result);
|
|
124
|
+
onChange?.(result);
|
|
125
|
+
};
|
|
126
|
+
return (_jsxs("w", { type: "STATIC", ws: 268435456 /* gui.WindowStyle.VISIBLE */ | 33554432 /* gui.WindowStyle.CLIPCHILDREN */, style: { ...style, flexDirection: 'row', alignItems: 'stretch' }, ref: (h) => {
|
|
127
|
+
containerRef.current = h;
|
|
128
|
+
if (typeof ref === 'function')
|
|
129
|
+
ref(h);
|
|
130
|
+
else if (ref)
|
|
131
|
+
ref.current = h;
|
|
132
|
+
}, children: [_jsx("w", { type: "EDIT", text: displayText, ws: 268435456 /* gui.WindowStyle.VISIBLE */ | 8388608 /* gui.WindowStyle.BORDER */ | 2048 /* gui.EditStyle.READONLY */ | 128 /* gui.EditStyle.AUTOHSCROLL */, style: { flexGrow: 1 } }), _jsx("w", { type: "BUTTON", text: "...", ws: 268435456 /* gui.WindowStyle.VISIBLE */, style: { width: 70 }, disabled: disabled, onEvent: (e) => {
|
|
133
|
+
if (e.msg === 514 /* gui.WmMsg.LBUTTONUP */)
|
|
134
|
+
handleBrowse();
|
|
135
|
+
} })] }));
|
|
136
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef, useEffect, useRef } from 'react';
|
|
3
|
+
import * as gui from 'gui';
|
|
4
|
+
export const ProgressBar = forwardRef(({ value = 0, max = 100, style, smooth }, ref) => {
|
|
5
|
+
const pbRef = useRef(null);
|
|
6
|
+
const ws = 268435456 /* gui.WindowStyle.VISIBLE */ | 8388608 /* gui.WindowStyle.BORDER */
|
|
7
|
+
| (smooth ? 1 /* gui.ProgressStyle.SMOOTH */ : 0);
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
const h = pbRef.current;
|
|
10
|
+
if (!h)
|
|
11
|
+
return;
|
|
12
|
+
gui.SendMessage(h, 1030 /* gui.ProgressMsg.SETRANGE32 */, 0, max);
|
|
13
|
+
gui.SendMessage(h, 1026 /* gui.ProgressMsg.SETPOS */, Math.min(value, max), 0);
|
|
14
|
+
}, [value, max]);
|
|
15
|
+
return (_jsx("w", { type: "msctls_progress32", ws: ws, style: style, ref: (h) => {
|
|
16
|
+
pbRef.current = h;
|
|
17
|
+
if (typeof ref === 'function')
|
|
18
|
+
ref(h);
|
|
19
|
+
else if (ref)
|
|
20
|
+
ref.current = h;
|
|
21
|
+
} }));
|
|
22
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef, useState, useEffect, useRef } from 'react';
|
|
3
|
+
import * as gui from 'gui';
|
|
4
|
+
export const RadioButton = forwardRef(({ checked: controlledChecked, defaultChecked = false, onChange, label, style, disabled }, ref) => {
|
|
5
|
+
const [internalChecked, setInternalChecked] = useState(defaultChecked);
|
|
6
|
+
const isControlled = controlledChecked !== undefined;
|
|
7
|
+
const displayChecked = isControlled ? controlledChecked : internalChecked;
|
|
8
|
+
const rbRef = useRef(null);
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
const h = rbRef.current;
|
|
11
|
+
if (!h)
|
|
12
|
+
return;
|
|
13
|
+
gui.SendMessage(h, 241 /* gui.ButtonMsg.SETCHECK */, displayChecked ? 1 /* gui.ButtonCheckState.CHECKED */ : 0 /* gui.ButtonCheckState.UNCHECKED */, 0);
|
|
14
|
+
}, [displayChecked]);
|
|
15
|
+
return (_jsx("w", { type: "BUTTON", text: label ?? '', ws: 268435456 /* gui.WindowStyle.VISIBLE */ | 9 /* gui.ButtonStyle.AUTORADIOBUTTON */, style: style, disabled: disabled, ref: (h) => {
|
|
16
|
+
rbRef.current = h;
|
|
17
|
+
if (typeof ref === 'function')
|
|
18
|
+
ref(h);
|
|
19
|
+
else if (ref)
|
|
20
|
+
ref.current = h;
|
|
21
|
+
}, onEvent: (e) => {
|
|
22
|
+
if (e.msg === 514 /* gui.WmMsg.LBUTTONUP */) {
|
|
23
|
+
const newChecked = gui.SendMessage(e.hwnd, 240 /* gui.ButtonMsg.GETCHECK */, 0, 0) !== 0;
|
|
24
|
+
if (newChecked !== displayChecked) {
|
|
25
|
+
if (!isControlled)
|
|
26
|
+
setInternalChecked(newChecked);
|
|
27
|
+
onChange?.(newChecked);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
} }));
|
|
31
|
+
});
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef, useLayoutEffect, useRef } from 'react';
|
|
3
|
+
import * as gui from 'gui';
|
|
4
|
+
import { forceFlexLayout } from '../reconciler.js';
|
|
5
|
+
export const ScrollView = forwardRef(({ children, style, contentWidth, contentHeight }, ref) => {
|
|
6
|
+
const svRef = useRef(null);
|
|
7
|
+
const contentRef = useRef(null);
|
|
8
|
+
const scrollXRef = useRef(0);
|
|
9
|
+
const scrollYRef = useRef(0);
|
|
10
|
+
// Win32 比例滚动条约定:
|
|
11
|
+
// min = 0
|
|
12
|
+
// max = contentSize - 1(内容最后像素行号)
|
|
13
|
+
// page = viewportSize(可见行数)
|
|
14
|
+
// nPos = 0 … max - page + 1(即 0 … contentSize - viewportSize)
|
|
15
|
+
function updateScroll(sv, content) {
|
|
16
|
+
const svRect = gui.GetClientRect(sv);
|
|
17
|
+
if (!svRect)
|
|
18
|
+
return;
|
|
19
|
+
const svW = svRect.right - svRect.left;
|
|
20
|
+
const svH = svRect.bottom - svRect.top;
|
|
21
|
+
const scale = gui.GetScaleFactor();
|
|
22
|
+
const natW = Math.round((contentWidth ?? (svW / scale)) * scale);
|
|
23
|
+
const natH = Math.round((contentHeight ?? (svH / scale)) * scale);
|
|
24
|
+
scrollXRef.current = Math.max(0, Math.min(scrollXRef.current, natW - svW));
|
|
25
|
+
scrollYRef.current = Math.max(0, Math.min(scrollYRef.current, natH - svH));
|
|
26
|
+
gui.SetWindowPos(content, 0 /* gui.SetWindowPosHwnd.TOP */, -scrollXRef.current, -scrollYRef.current, natW, natH, 4 /* gui.SetWindowPosFlag.SWP_NOZORDER */);
|
|
27
|
+
forceFlexLayout(content);
|
|
28
|
+
if (natW > svW) {
|
|
29
|
+
gui.SetScrollInfo(sv, 0 /* gui.ScrollBar.HORZ */, { min: 0, max: natW - 1, page: svW, pos: scrollXRef.current }, true);
|
|
30
|
+
gui.ShowScrollBar(sv, 0 /* gui.ScrollBar.HORZ */, true);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
scrollXRef.current = 0;
|
|
34
|
+
gui.ShowScrollBar(sv, 0 /* gui.ScrollBar.HORZ */, false);
|
|
35
|
+
}
|
|
36
|
+
if (natH > svH) {
|
|
37
|
+
gui.SetScrollInfo(sv, 1 /* gui.ScrollBar.VERT */, { min: 0, max: natH - 1, page: svH, pos: scrollYRef.current }, true);
|
|
38
|
+
gui.ShowScrollBar(sv, 1 /* gui.ScrollBar.VERT */, true);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
scrollYRef.current = 0;
|
|
42
|
+
gui.ShowScrollBar(sv, 1 /* gui.ScrollBar.VERT */, false);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
useLayoutEffect(() => {
|
|
46
|
+
const sv = svRef.current;
|
|
47
|
+
const content = contentRef.current;
|
|
48
|
+
if (!sv || !content)
|
|
49
|
+
return;
|
|
50
|
+
updateScroll(sv, content);
|
|
51
|
+
}, [contentWidth, contentHeight]);
|
|
52
|
+
function setScrollPos(sv, content, bar, newPos) {
|
|
53
|
+
if (bar === 1 /* gui.ScrollBar.VERT */) {
|
|
54
|
+
scrollYRef.current = newPos;
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
scrollXRef.current = newPos;
|
|
58
|
+
}
|
|
59
|
+
gui.SetWindowPos(content, 0 /* gui.SetWindowPosHwnd.TOP */, -scrollXRef.current, -scrollYRef.current, 0, 0, 1 /* gui.SetWindowPosFlag.SWP_NOSIZE */ | 4 /* gui.SetWindowPosFlag.SWP_NOZORDER */);
|
|
60
|
+
gui.SetScrollInfo(sv, bar, { pos: newPos }, true);
|
|
61
|
+
}
|
|
62
|
+
function handleScroll(e, bar) {
|
|
63
|
+
const sv = svRef.current;
|
|
64
|
+
const content = contentRef.current;
|
|
65
|
+
if (!sv || !content)
|
|
66
|
+
return;
|
|
67
|
+
const info = gui.GetScrollInfo(sv, bar);
|
|
68
|
+
const maxPos = info.max - info.page + 1;
|
|
69
|
+
const code = e.wParam & 0xFFFF;
|
|
70
|
+
const ref_ = bar === 1 /* gui.ScrollBar.VERT */ ? scrollYRef : scrollXRef;
|
|
71
|
+
let newPos = ref_.current;
|
|
72
|
+
if (code === 5 /* gui.ScrollCmd.THUMBTRACK */) {
|
|
73
|
+
newPos = Math.max(0, Math.min(maxPos, (e.wParam >> 16) & 0xFFFF));
|
|
74
|
+
}
|
|
75
|
+
else if (code === 0 /* gui.ScrollCmd.LINEUP */)
|
|
76
|
+
newPos -= 20;
|
|
77
|
+
else if (code === 1 /* gui.ScrollCmd.LINEDOWN */)
|
|
78
|
+
newPos += 20;
|
|
79
|
+
else if (code === 2 /* gui.ScrollCmd.PAGEUP */)
|
|
80
|
+
newPos -= info.page;
|
|
81
|
+
else if (code === 3 /* gui.ScrollCmd.PAGEDOWN */)
|
|
82
|
+
newPos += info.page;
|
|
83
|
+
else
|
|
84
|
+
return;
|
|
85
|
+
newPos = Math.max(0, Math.min(maxPos, newPos));
|
|
86
|
+
if (newPos !== ref_.current)
|
|
87
|
+
setScrollPos(sv, content, bar, newPos);
|
|
88
|
+
}
|
|
89
|
+
function handleMouseWheel(e) {
|
|
90
|
+
const sv = svRef.current;
|
|
91
|
+
const content = contentRef.current;
|
|
92
|
+
if (!sv || !content)
|
|
93
|
+
return;
|
|
94
|
+
const wheel = e.wParam >> 16;
|
|
95
|
+
const dy = -Math.round(wheel * 40 / 120);
|
|
96
|
+
const isHorz = (e.wParam & 4 /* gui.MouseKeyFlag.MK_SHIFT */) !== 0;
|
|
97
|
+
if (isHorz && contentWidth === undefined)
|
|
98
|
+
return;
|
|
99
|
+
if (!isHorz && contentHeight === undefined)
|
|
100
|
+
return;
|
|
101
|
+
const bar = isHorz ? 0 /* gui.ScrollBar.HORZ */ : 1 /* gui.ScrollBar.VERT */;
|
|
102
|
+
const info = gui.GetScrollInfo(sv, bar);
|
|
103
|
+
const maxPos = info.max - info.page + 1;
|
|
104
|
+
const ref_ = isHorz ? scrollXRef : scrollYRef;
|
|
105
|
+
let newPos = ref_.current + dy;
|
|
106
|
+
newPos = Math.max(0, Math.min(maxPos, newPos));
|
|
107
|
+
if (newPos !== ref_.current)
|
|
108
|
+
setScrollPos(sv, content, bar, newPos);
|
|
109
|
+
}
|
|
110
|
+
return (_jsx("w", { type: "STATIC", ws: 268435456 /* gui.WindowStyle.VISIBLE */ | 33554432 /* gui.WindowStyle.CLIPCHILDREN */ |
|
|
111
|
+
(contentWidth !== undefined ? 1048576 /* gui.WindowStyle.HSCROLL */ : 0) |
|
|
112
|
+
(contentHeight !== undefined ? 2097152 /* gui.WindowStyle.VSCROLL */ : 0), style: style, ref: (h) => {
|
|
113
|
+
svRef.current = h;
|
|
114
|
+
if (typeof ref === 'function')
|
|
115
|
+
ref(h);
|
|
116
|
+
else if (ref)
|
|
117
|
+
ref.current = h;
|
|
118
|
+
}, onEvent: {
|
|
119
|
+
fn: (e) => {
|
|
120
|
+
if (e.msg === 132 /* gui.WmMsg.NCHITTEST */ ||
|
|
121
|
+
e.msg === 161 /* gui.WmMsg.NCLBUTTONDOWN */) {
|
|
122
|
+
return gui.DefWindowProc(e.hwnd, e.msg, e.wParam, e.lParam);
|
|
123
|
+
}
|
|
124
|
+
if (e.msg === 277 /* gui.WmMsg.VSCROLL */) {
|
|
125
|
+
handleScroll(e, 1 /* gui.ScrollBar.VERT */);
|
|
126
|
+
return 0;
|
|
127
|
+
}
|
|
128
|
+
if (e.msg === 276 /* gui.WmMsg.HSCROLL */) {
|
|
129
|
+
handleScroll(e, 0 /* gui.ScrollBar.HORZ */);
|
|
130
|
+
return 0;
|
|
131
|
+
}
|
|
132
|
+
if (e.msg === 522 /* gui.WmMsg.MOUSEWHEEL */) {
|
|
133
|
+
handleMouseWheel(e);
|
|
134
|
+
return 0;
|
|
135
|
+
}
|
|
136
|
+
return e.callOldWndProc();
|
|
137
|
+
}
|
|
138
|
+
}, children: _jsx("w", { type: "STATIC", ws: 268435456 /* gui.WindowStyle.VISIBLE */ | 33554432 /* gui.WindowStyle.CLIPCHILDREN */, style: { flexDirection: 'column', alignItems: 'flex-start' }, ref: (h) => { contentRef.current = h; }, children: children }) }));
|
|
139
|
+
});
|