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.
Files changed (175) hide show
  1. package/README.en.md +197 -0
  2. package/README.md +196 -0
  3. package/examples/PdfCanvas.js +67 -0
  4. package/examples/PdfCanvas.tsx +83 -0
  5. package/examples/pdf_preview2.js +431 -0
  6. package/examples/pdf_preview2.ts +375 -0
  7. package/examples/pdf_viewer.js +17623 -0
  8. package/examples/pdf_viewer.tsx +279 -0
  9. package/examples/test_gallery.js +17452 -0
  10. package/examples/test_gallery.tsx +285 -0
  11. package/examples/test_react_lazy.js +17336 -0
  12. package/examples/test_react_lazy.tsx +63 -0
  13. package/examples/test_react_listbox.js +17339 -0
  14. package/examples/test_react_listbox.tsx +71 -0
  15. package/examples/test_root_window.js +17313 -0
  16. package/examples/test_root_window.tsx +41 -0
  17. package/examples/test_tab.js +17339 -0
  18. package/examples/test_tab.tsx +75 -0
  19. package/examples/tray_demo.js +79 -0
  20. package/examples/tray_demo.tsx +81 -0
  21. package/lib/assert.js +8 -0
  22. package/lib/assert.ts +7 -0
  23. package/lib/cache-utils.js +43 -0
  24. package/lib/cache-utils.ts +40 -0
  25. package/lib/dumpRects.js +25 -0
  26. package/lib/dumpRects.ts +25 -0
  27. package/lib/fetch.js +490 -0
  28. package/lib/fetch.ts +502 -0
  29. package/lib/http-common.js +342 -0
  30. package/lib/http-common.ts +399 -0
  31. package/lib/http-server.js +416 -0
  32. package/lib/http-server.ts +424 -0
  33. package/lib/polyfill.js +156 -0
  34. package/lib/polyfill.ts +171 -0
  35. package/lib/react-qw/components/Button.js +8 -0
  36. package/lib/react-qw/components/CheckBox.js +31 -0
  37. package/lib/react-qw/components/ComboBox.js +42 -0
  38. package/lib/react-qw/components/DateTimePicker.js +96 -0
  39. package/lib/react-qw/components/Input.js +50 -0
  40. package/lib/react-qw/components/Link.js +43 -0
  41. package/lib/react-qw/components/ListBox.js +48 -0
  42. package/lib/react-qw/components/ListView.js +328 -0
  43. package/lib/react-qw/components/PathPicker.js +136 -0
  44. package/lib/react-qw/components/ProgressBar.js +22 -0
  45. package/lib/react-qw/components/RadioButton.js +31 -0
  46. package/lib/react-qw/components/ScrollView.js +139 -0
  47. package/lib/react-qw/components/Slider.js +45 -0
  48. package/lib/react-qw/components/Tab.js +61 -0
  49. package/lib/react-qw/components/Tooltip.js +55 -0
  50. package/lib/react-qw/components/TreeView.js +130 -0
  51. package/lib/react-qw/components/index.js +16 -0
  52. package/lib/react-qw/components/index.ts +17 -0
  53. package/lib/react-qw/index.js +73 -0
  54. package/lib/react-qw/index.ts +90 -0
  55. package/lib/react-qw/jsx.d.ts +34 -0
  56. package/lib/react-qw/layout.js +82 -0
  57. package/lib/react-qw/layout.ts +111 -0
  58. package/lib/react-qw/props.js +27 -0
  59. package/lib/react-qw/props.ts +36 -0
  60. package/lib/react-qw/reconciler.js +333 -0
  61. package/lib/react-qw/reconciler.ts +373 -0
  62. package/lib/stream.js +2 -0
  63. package/lib/stream.ts +22 -0
  64. package/lib/text-codec.js +197 -0
  65. package/lib/text-codec.ts +171 -0
  66. package/lib/text-measure.js +52 -0
  67. package/lib/text-measure.ts +54 -0
  68. package/lib/url.js +278 -0
  69. package/lib/url.ts +201 -0
  70. package/lib/vendor/web-streams/ponyfill.d.mts +829 -0
  71. package/lib/vendor/web-streams/ponyfill.mjs +8 -0
  72. package/lib/websocket.js +541 -0
  73. package/lib/websocket.ts +555 -0
  74. package/package.json +44 -0
  75. package/quickwin.d.ts +594 -0
  76. package/quickwin_const.d.ts +900 -0
  77. package/qwin-nowasm.exe +0 -0
  78. package/qwin.exe +0 -0
  79. package/test/add.wasm +0 -0
  80. package/test/complex.wasm +0 -0
  81. package/test/complex_imports.wasm +0 -0
  82. package/test/debug/http_server_e2e.js +31 -0
  83. package/test/debug/minwasm.js +52 -0
  84. package/test/debug/mupdf_debug.js +104 -0
  85. package/test/debug/wasm.js +122 -0
  86. package/test/debug/wasm_export_debug.js +64 -0
  87. package/test/debug/wasm_names.js +46 -0
  88. package/test/global_imports.wasm +0 -0
  89. package/test/import_func.wasm +0 -0
  90. package/test/imports.wasm +0 -0
  91. package/test/run.js +91 -0
  92. package/test/run.ts +96 -0
  93. package/test/sjlj.wasm +0 -0
  94. package/test/test_basic.js +7 -0
  95. package/test/test_basic.ts +9 -0
  96. package/test/test_basic_fetch.js +34 -0
  97. package/test/test_basic_fetch.ts +33 -0
  98. package/test/test_brotli.js +48 -0
  99. package/test/test_brotli.ts +52 -0
  100. package/test/test_fetch_cache.js +169 -0
  101. package/test/test_fetch_cache.ts +187 -0
  102. package/test/test_ffi.js +157 -0
  103. package/test/test_ffi.ts +174 -0
  104. package/test/test_frame_encoding.js +128 -0
  105. package/test/test_frame_encoding.ts +132 -0
  106. package/test/test_helper.js +84 -0
  107. package/test/test_helper.ts +80 -0
  108. package/test/test_http_import.js +78 -0
  109. package/test/test_http_import.ts +74 -0
  110. package/test/test_http_server.js +142 -0
  111. package/test/test_http_server.ts +136 -0
  112. package/test/test_import_dir.js +4 -0
  113. package/test/test_import_dir.ts +5 -0
  114. package/test/test_ipv6.js +134 -0
  115. package/test/test_ipv6.ts +129 -0
  116. package/test/test_jsx.js +1314 -0
  117. package/test/test_meta_url.js +3 -0
  118. package/test/test_meta_url.ts +4 -0
  119. package/test/test_mupdf_render.js +69 -0
  120. package/test/test_mupdf_render.ts +74 -0
  121. package/test/test_mupdf_twice.js +77 -0
  122. package/test/test_mupdf_twice.ts +81 -0
  123. package/test/test_mupdf_wasm.js +33 -0
  124. package/test/test_mupdf_wasm.ts +30 -0
  125. package/test/test_net_event.js +57 -0
  126. package/test/test_net_event.ts +55 -0
  127. package/test/test_net_fetch.js +259 -0
  128. package/test/test_net_fetch.ts +238 -0
  129. package/test/test_net_websocket.js +180 -0
  130. package/test/test_net_websocket.ts +164 -0
  131. package/test/test_polyfill.js +144 -0
  132. package/test/test_polyfill.ts +159 -0
  133. package/test/test_react_complex.js +17440 -0
  134. package/test/test_react_counter.js +17331 -0
  135. package/test/test_react_flex.js +17643 -0
  136. package/test/test_react_hidden.js +17336 -0
  137. package/test/test_react_listview_link.js +17371 -0
  138. package/test/test_react_render.js +17326 -0
  139. package/test/test_url.js +173 -0
  140. package/test/test_url.ts +183 -0
  141. package/test/test_wasm_basic.js +85 -0
  142. package/test/test_wasm_basic.ts +73 -0
  143. package/test/test_wasm_import_global.js +41 -0
  144. package/test/test_wasm_import_global.ts +45 -0
  145. package/test/test_wasm_sjlj.js +145 -0
  146. package/test/test_wasm_sjlj.ts +125 -0
  147. package/test/test_wasm_types.js +97 -0
  148. package/test/test_wasm_types.ts +124 -0
  149. package/test/test_worker.js +21 -0
  150. package/test/test_worker.ts +24 -0
  151. package/test/test_worker_fetch.js +26 -0
  152. package/test/test_worker_fetch.ts +27 -0
  153. package/test/test_worker_net.js +51 -0
  154. package/test/test_worker_net.ts +54 -0
  155. package/test/test_worker_wasm_concurrent.js +44 -0
  156. package/test/test_worker_wasm_concurrent.ts +45 -0
  157. package/test/types.wasm +0 -0
  158. package/test/worker_fetch.js +13 -0
  159. package/test/worker_fetch.ts +13 -0
  160. package/test/worker_helper.js +1 -0
  161. package/test/worker_helper.ts +1 -0
  162. package/test/worker_http.js +18 -0
  163. package/test/worker_http.ts +20 -0
  164. package/test/worker_import_lib.js +12 -0
  165. package/test/worker_import_lib.ts +13 -0
  166. package/test/worker_module.js +16 -0
  167. package/test/worker_module.ts +17 -0
  168. package/test/worker_wasm_concurrent.js +22 -0
  169. package/test/worker_wasm_concurrent.ts +22 -0
  170. package/tsconfig.json +27 -0
  171. package/vendor/mupdf-wasm/mupdf-wasm.d.ts +571 -0
  172. package/vendor/mupdf-wasm/mupdf-wasm.js +2749 -0
  173. package/vendor/mupdf-wasm/mupdf-wasm.wasm +0 -0
  174. package/vendor/mupdf-wasm/mupdf.d.ts +939 -0
  175. package/vendor/mupdf-wasm/mupdf.js +3317 -0
@@ -0,0 +1,431 @@
1
+ import '../lib/polyfill.js';
2
+ import * as std from 'std';
3
+ import * as gui from 'gui';
4
+ import * as win from 'win';
5
+ import * as ffi from 'ffi';
6
+ import { assertNonNullable } from '../lib/assert.js';
7
+ const FFI_PTR = ffi.FFI_TYPE_POINTER;
8
+ const FFI_U32 = ffi.FFI_TYPE_UINT32;
9
+ const FFI_S32 = ffi.FFI_TYPE_SINT32;
10
+ const _user32 = win.LoadLibrary('user32.dll');
11
+ const _gdi32 = win.LoadLibrary('gdi32.dll');
12
+ const _comdlg32 = win.LoadLibrary('comdlg32.dll');
13
+ if (!(_user32 && _gdi32 && _comdlg32))
14
+ std.exit(0);
15
+ function loadProc(lib, name) {
16
+ const ptr = win.GetProcAddress(lib, name);
17
+ if (!ptr) {
18
+ std.printf('Error: cannot load %s\n', name);
19
+ std.exit(1);
20
+ }
21
+ return ptr;
22
+ }
23
+ const GetOpenFileNameW = loadProc(_comdlg32, 'GetOpenFileNameW');
24
+ const SetDIBitsToDevice = loadProc(_gdi32, 'SetDIBitsToDevice');
25
+ const GetDC = loadProc(_user32, 'GetDC');
26
+ const ReleaseDC = loadProc(_user32, 'ReleaseDC');
27
+ const PatBlt = loadProc(_gdi32, 'PatBlt');
28
+ const WHITENESS = 0x00FF0062;
29
+ function makeBitmapInfo(w, h) {
30
+ const bmi = new ArrayBuffer(40);
31
+ const bv = new DataView(bmi);
32
+ bv.setUint32(0, 40, true);
33
+ bv.setInt32(4, w, true);
34
+ bv.setInt32(8, -h, true);
35
+ bv.setUint16(12, 1, true);
36
+ bv.setUint16(14, 24, true);
37
+ return bmi;
38
+ }
39
+ function strToWide(str) {
40
+ return new TextEncoder('utf-16le').encode(str + '\0').buffer;
41
+ }
42
+ function wideToStr(buf) {
43
+ const dv = new DataView(buf);
44
+ const chars = [];
45
+ for (let i = 0; i < buf.byteLength; i += 2) {
46
+ const c = dv.getUint16(i, true);
47
+ if (c === 0)
48
+ break;
49
+ chars.push(c);
50
+ }
51
+ return String.fromCharCode(...chars);
52
+ }
53
+ function setPtr(dv, off, ptr) {
54
+ dv.setUint32(off, ptr & 0xFFFFFFFF, true);
55
+ dv.setUint32(off + 4, Math.floor(ptr / 0x100000000), true);
56
+ }
57
+ let hwndMain = null;
58
+ let hwndEdit = null;
59
+ let hwndCanvas = null;
60
+ let hwndContent = null;
61
+ let currentPixmap = null;
62
+ let currentPage = 0, totalPages = 0;
63
+ let scrollX = 0, scrollY = 0;
64
+ let cachedPath = '';
65
+ let cachedDoc = null;
66
+ function clearCachedDoc() {
67
+ if (cachedDoc) {
68
+ try {
69
+ cachedDoc.destroy();
70
+ }
71
+ catch { }
72
+ }
73
+ cachedDoc = null;
74
+ cachedPath = '';
75
+ }
76
+ async function loadMupdf() {
77
+ const wasmPath = './vendor/mupdf-wasm/mupdf-wasm.wasm';
78
+ const fp = std.open(wasmPath, 'rb');
79
+ if (!fp) {
80
+ std.printf('Error: cannot open %s\n', wasmPath);
81
+ return null;
82
+ }
83
+ fp.seek(0, 2);
84
+ const size = fp.tell();
85
+ fp.seek(0, 0);
86
+ const buf = new ArrayBuffer(size);
87
+ fp.read(buf, 0, size);
88
+ fp.close();
89
+ (globalThis).$libmupdf_wasm_Module = { wasmBinary: buf, locateFile: (p) => p };
90
+ try {
91
+ return await import('../vendor/mupdf-wasm/mupdf.js');
92
+ }
93
+ catch (e) {
94
+ std.printf('Error: mupdf load failed: %s\n', String(e));
95
+ return null;
96
+ }
97
+ }
98
+ function renderPdfPage(mupdf, filePath, pageIndex) {
99
+ if (filePath !== cachedPath) {
100
+ clearCachedDoc();
101
+ const fp = std.open(filePath, 'rb');
102
+ if (!fp) {
103
+ std.printf('Error: cannot open %s\n', filePath);
104
+ return null;
105
+ }
106
+ let buf = null;
107
+ try {
108
+ fp.seek(0, 2);
109
+ const size = fp.tell();
110
+ fp.seek(0, 0);
111
+ buf = new ArrayBuffer(size);
112
+ fp.read(buf, 0, size);
113
+ }
114
+ finally {
115
+ fp.close();
116
+ }
117
+ if (!buf)
118
+ return null;
119
+ try {
120
+ cachedDoc = mupdf.Document.openDocument(new Uint8Array(buf), 'application/pdf');
121
+ cachedPath = filePath;
122
+ }
123
+ catch (e) {
124
+ std.printf('Error opening document: %s\n', String(e));
125
+ return null;
126
+ }
127
+ }
128
+ if (!cachedDoc)
129
+ return null;
130
+ let page = null;
131
+ let pixmap = null;
132
+ try {
133
+ page = cachedDoc.loadPage(pageIndex);
134
+ pixmap = page.toPixmap(mupdf.Matrix.scale(1.5, 1.5), mupdf.ColorSpace.DeviceRGB, false);
135
+ if (!pixmap)
136
+ return null;
137
+ const srcPixels = pixmap.getPixels();
138
+ const srcStride = pixmap.getStride();
139
+ const w = pixmap.getWidth(), h = pixmap.getHeight();
140
+ const dibStride = Math.floor((w * 3 + 3) / 4) * 4;
141
+ const dib = new Uint8Array(h * dibStride);
142
+ for (let y = 0; y < h; y++) {
143
+ const srcOff = y * srcStride, dstOff = y * dibStride;
144
+ for (let x = 0; x < w; x++) {
145
+ const sx = srcOff + x * 3, dx = dstOff + x * 3;
146
+ dib[dx] = srcPixels[sx + 2];
147
+ dib[dx + 1] = srcPixels[sx + 1];
148
+ dib[dx + 2] = srcPixels[sx];
149
+ }
150
+ }
151
+ const totalPages = cachedDoc.countPages();
152
+ return { data: dib.buffer, w, h, totalPages };
153
+ }
154
+ catch (e) {
155
+ std.printf('Error rendering: %s\n', String(e));
156
+ clearCachedDoc();
157
+ return null;
158
+ }
159
+ finally {
160
+ if (pixmap)
161
+ try {
162
+ pixmap.destroy();
163
+ }
164
+ catch { }
165
+ ;
166
+ if (page)
167
+ try {
168
+ page.destroy();
169
+ }
170
+ catch { }
171
+ }
172
+ }
173
+ function openPdfFileDialog() {
174
+ assertNonNullable(hwndMain);
175
+ const structBuf = new ArrayBuffer(152), sv = new DataView(structBuf);
176
+ const fileBuf = new ArrayBuffer(260 * 2);
177
+ const filterWide = strToWide('PDF Files\0*.pdf\0All Files\0*.*\0\0');
178
+ sv.setUint32(0, 152, true);
179
+ sv.setUint32(8, hwndMain & 0xFFFFFFFF, true);
180
+ sv.setUint32(12, Math.floor(hwndMain / 0x100000000), true);
181
+ setPtr(sv, 24, ffi.bufferPtr(filterWide));
182
+ setPtr(sv, 48, ffi.bufferPtr(fileBuf));
183
+ sv.setUint32(56, 260, true);
184
+ sv.setUint32(96, 0x1000 | 0x0800 | 0x0004, true);
185
+ const ret = ffi.ffiCall(GetOpenFileNameW, [FFI_PTR], [structBuf], FFI_U32);
186
+ return ret ? wideToStr(fileBuf) : null;
187
+ }
188
+ function updateScrollRange() {
189
+ assertNonNullable(hwndCanvas);
190
+ assertNonNullable(hwndContent);
191
+ if (!currentPixmap)
192
+ return;
193
+ const cr = gui.GetClientRect(hwndCanvas);
194
+ if (!cr)
195
+ return;
196
+ const cw = cr.right - cr.left, ch = cr.bottom - cr.top;
197
+ const maxX = Math.max(0, currentPixmap.w - cw);
198
+ const maxY = Math.max(0, currentPixmap.h - ch);
199
+ scrollX = Math.min(scrollX, maxX);
200
+ scrollY = Math.min(scrollY, maxY);
201
+ if (currentPixmap.w > cw) {
202
+ const style = gui.GetWindowLongPtr(hwndCanvas, -16 /* gui.Gwlp.STYLE */) | 0;
203
+ if (!(style & 1048576 /* gui.WindowStyle.HSCROLL */)) {
204
+ gui.SetWindowLongPtr(hwndCanvas, -16 /* gui.Gwlp.STYLE */, style | 1048576 /* gui.WindowStyle.HSCROLL */);
205
+ gui.SetWindowPos(hwndCanvas, 0 /* gui.SetWindowPosHwnd.TOP */, 0, 0, 0, 0, 2 /* gui.SetWindowPosFlag.SWP_NOMOVE */ | 1 /* gui.SetWindowPosFlag.SWP_NOSIZE */ | 4 /* gui.SetWindowPosFlag.SWP_NOZORDER */ | 32 /* gui.SetWindowPosFlag.SWP_FRAMECHANGED */);
206
+ }
207
+ gui.SetScrollInfo(hwndCanvas, 0 /* gui.ScrollBar.HORZ */, { min: 0, max: currentPixmap.w - 1, page: cw, pos: scrollX }, true);
208
+ gui.ShowScrollBar(hwndCanvas, 0 /* gui.ScrollBar.HORZ */, true);
209
+ }
210
+ else {
211
+ scrollX = 0;
212
+ const style = gui.GetWindowLongPtr(hwndCanvas, -16 /* gui.Gwlp.STYLE */) | 0;
213
+ if (style & 1048576 /* gui.WindowStyle.HSCROLL */) {
214
+ gui.SetWindowLongPtr(hwndCanvas, -16 /* gui.Gwlp.STYLE */, style & ~1048576 /* gui.WindowStyle.HSCROLL */);
215
+ gui.SetWindowPos(hwndCanvas, 0 /* gui.SetWindowPosHwnd.TOP */, 0, 0, 0, 0, 2 /* gui.SetWindowPosFlag.SWP_NOMOVE */ | 1 /* gui.SetWindowPosFlag.SWP_NOSIZE */ | 4 /* gui.SetWindowPosFlag.SWP_NOZORDER */ | 32 /* gui.SetWindowPosFlag.SWP_FRAMECHANGED */);
216
+ }
217
+ gui.ShowScrollBar(hwndCanvas, 0 /* gui.ScrollBar.HORZ */, false);
218
+ }
219
+ if (currentPixmap.h > ch) {
220
+ gui.SetScrollInfo(hwndCanvas, 1 /* gui.ScrollBar.VERT */, { min: 0, max: currentPixmap.h - 1, page: ch, pos: scrollY }, true);
221
+ gui.ShowScrollBar(hwndCanvas, 1 /* gui.ScrollBar.VERT */, true);
222
+ }
223
+ else {
224
+ scrollY = 0;
225
+ gui.ShowScrollBar(hwndCanvas, 1 /* gui.ScrollBar.VERT */, false);
226
+ }
227
+ gui.SetWindowPos(hwndContent, 0 /* gui.SetWindowPosHwnd.TOP */, -scrollX, -scrollY, 0, 0, 1 /* gui.SetWindowPosFlag.SWP_NOSIZE */ | 4 /* gui.SetWindowPosFlag.SWP_NOZORDER */);
228
+ }
229
+ function showPdf(mupdf, path, pageIdx) {
230
+ assertNonNullable(hwndMain);
231
+ assertNonNullable(hwndContent);
232
+ const pix = renderPdfPage(mupdf, path, pageIdx);
233
+ if (!pix) {
234
+ gui.MessageBox('渲染 PDF 失败');
235
+ return;
236
+ }
237
+ currentPixmap = pix;
238
+ currentPage = pageIdx;
239
+ totalPages = pix.totalPages;
240
+ gui.SetWindowText(hwndMain, 'PDF 预览 - 第 ' + (pageIdx + 1) + '/' + totalPages + ' 页');
241
+ scrollX = 0;
242
+ scrollY = 0;
243
+ gui.SetWindowPos(hwndContent, 0 /* gui.SetWindowPosHwnd.TOP */, 0, 0, pix.w, pix.h, 4 /* gui.SetWindowPosFlag.SWP_NOZORDER */ | 2 /* gui.SetWindowPosFlag.SWP_NOMOVE */);
244
+ updateScrollRange();
245
+ gui.InvalidateRect(hwndContent, null, true);
246
+ }
247
+ function doScroll(dx, dy) {
248
+ assertNonNullable(hwndCanvas);
249
+ assertNonNullable(hwndContent);
250
+ const cr = gui.GetClientRect(hwndCanvas);
251
+ if (!cr || !currentPixmap)
252
+ return;
253
+ const cw = cr.right - cr.left, ch = cr.bottom - cr.top;
254
+ const maxX = Math.max(0, currentPixmap.w - cw);
255
+ const maxY = Math.max(0, currentPixmap.h - ch);
256
+ scrollX = Math.max(0, Math.min(maxX, scrollX + dx));
257
+ scrollY = Math.max(0, Math.min(maxY, scrollY + dy));
258
+ gui.SetWindowPos(hwndContent, 0 /* gui.SetWindowPosHwnd.TOP */, -scrollX, -scrollY, 0, 0, 1 /* gui.SetWindowPosFlag.SWP_NOSIZE */ | 4 /* gui.SetWindowPosFlag.SWP_NOZORDER */);
259
+ gui.SetScrollInfo(hwndCanvas, 0 /* gui.ScrollBar.HORZ */, { pos: scrollX }, true);
260
+ gui.SetScrollInfo(hwndCanvas, 1 /* gui.ScrollBar.VERT */, { pos: scrollY }, true);
261
+ }
262
+ async function main() {
263
+ const mupdf = await loadMupdf();
264
+ if (!mupdf) {
265
+ gui.MessageBox('加载 mupdf WASM 失败');
266
+ return;
267
+ }
268
+ const WS_CHILD = 1073741824 /* gui.WindowStyle.CHILD */;
269
+ const WS_VIS = 268435456 /* gui.WindowStyle.VISIBLE */;
270
+ const WS_BORDER = 8388608 /* gui.WindowStyle.BORDER */;
271
+ const ctrlY = 12, ctrlH = 26, gap = 4, btnW = 80, btnPageW = 72;
272
+ let hwndBtnOpen = null;
273
+ let hwndBtnPrev = null;
274
+ let hwndBtnNext = null;
275
+ gui.RegisterClass('PdfViewer2', (hwnd, msg, wParam, lParam) => {
276
+ assertNonNullable(hwndCanvas);
277
+ assertNonNullable(hwndEdit);
278
+ assertNonNullable(hwndBtnOpen);
279
+ assertNonNullable(hwndBtnPrev);
280
+ assertNonNullable(hwndBtnNext);
281
+ if (msg === 2 /* gui.WmMsg.DESTROY */) {
282
+ gui.PostQuitMessage(0);
283
+ return 0;
284
+ }
285
+ if (msg === 5 /* gui.WmMsg.SIZE */) {
286
+ const cr = gui.GetClientRect(hwnd);
287
+ if (cr) {
288
+ const cw = cr.right - cr.left, ch = cr.bottom - cr.top;
289
+ gui.SetWindowPos(hwndCanvas, 0 /* gui.SetWindowPosHwnd.TOP */, 0, 50, cw, Math.max(1, ch - 50), 4 /* gui.SetWindowPosFlag.SWP_NOZORDER */);
290
+ gui.SetWindowPos(hwndEdit, 0 /* gui.SetWindowPosHwnd.TOP */, 0, 0, Math.max(100, cw - 320), 0, 2 /* gui.SetWindowPosFlag.SWP_NOMOVE */ | 4 /* gui.SetWindowPosFlag.SWP_NOZORDER */);
291
+ }
292
+ return gui.DefWindowProc(hwnd, msg, wParam, lParam);
293
+ }
294
+ if (msg === 273 /* gui.WmMsg.COMMAND */) {
295
+ const hCtrl = lParam;
296
+ if (hCtrl === hwndBtnOpen) {
297
+ const path = openPdfFileDialog();
298
+ if (path) {
299
+ gui.SetWindowText(hwndEdit, path);
300
+ showPdf(mupdf, path, 0);
301
+ }
302
+ }
303
+ else if (hCtrl === hwndBtnPrev) {
304
+ const path = gui.GetWindowText(hwndEdit);
305
+ if (path && currentPage > 0)
306
+ showPdf(mupdf, path, currentPage - 1);
307
+ }
308
+ else if (hCtrl === hwndBtnNext) {
309
+ const path = gui.GetWindowText(hwndEdit);
310
+ if (path && currentPage < totalPages - 1)
311
+ showPdf(mupdf, path, currentPage + 1);
312
+ }
313
+ return 0;
314
+ }
315
+ return gui.DefWindowProc(hwnd, msg, wParam, lParam);
316
+ });
317
+ hwndMain = gui.CreateWindow('PdfViewer2', 'PDF 预览 2', 13565952 /* gui.WindowStyle.OVERLAPPEDWINDOW */ | 33554432 /* gui.WindowStyle.CLIPCHILDREN */, 100, 100, 1100, 800, null, null);
318
+ if (!hwndMain) {
319
+ gui.MessageBox('创建主窗口失败');
320
+ return;
321
+ }
322
+ // 控件直接挂在主窗口上
323
+ hwndEdit = gui.CreateWindow('EDIT', '', WS_CHILD | WS_VIS | WS_BORDER, ctrlY + btnW + gap, ctrlY, 480, ctrlH, hwndMain, null);
324
+ hwndBtnOpen = gui.CreateWindow('BUTTON', '打开 PDF', WS_CHILD | WS_VIS, ctrlY, ctrlY, btnW, ctrlH, hwndMain, null);
325
+ hwndBtnPrev = gui.CreateWindow('BUTTON', '上一页', WS_CHILD | WS_VIS, ctrlY + btnW + gap + 480 + gap, ctrlY, btnPageW, ctrlH, hwndMain, null);
326
+ hwndBtnNext = gui.CreateWindow('BUTTON', '下一页', WS_CHILD | WS_VIS, ctrlY + btnW + gap + 480 + gap + btnPageW + gap, ctrlY, btnPageW, ctrlH, hwndMain, null);
327
+ // 画布(带滚动条)
328
+ hwndCanvas = gui.CreateWindow('STATIC', '', WS_CHILD | WS_VIS | 33554432 /* gui.WindowStyle.CLIPCHILDREN */ | 2097152 /* gui.WindowStyle.VSCROLL */ | 1048576 /* gui.WindowStyle.HSCROLL */, 0, 50, 1100, 750, hwndMain, null);
329
+ // 内容窗口(画 pixmap)
330
+ hwndContent = gui.CreateWindow('STATIC', '', WS_CHILD | WS_VIS, 0, 0, 100, 100, hwndCanvas, null);
331
+ assertNonNullable(hwndMain);
332
+ assertNonNullable(hwndEdit);
333
+ assertNonNullable(hwndCanvas);
334
+ assertNonNullable(hwndContent);
335
+ assertNonNullable(hwndBtnOpen);
336
+ assertNonNullable(hwndBtnPrev);
337
+ assertNonNullable(hwndBtnNext);
338
+ // 内容窗口 PAINT
339
+ gui.SetWindowProc(hwndContent, (hwnd, msg, wParam, lParam) => {
340
+ if (msg === 20 /* gui.WmMsg.ERASEBKGND */)
341
+ return 1;
342
+ if (msg === 15 /* gui.WmMsg.PAINT */) {
343
+ gui.DefWindowProc(hwnd, msg, wParam, lParam);
344
+ const pm = currentPixmap;
345
+ if (!pm)
346
+ return 0;
347
+ const hdc = ffi.ffiCall(GetDC, [ffi.FFI_TYPE_UINT64], [hwnd], ffi.FFI_TYPE_UINT64);
348
+ if (hdc) {
349
+ const bmi = makeBitmapInfo(pm.w, pm.h);
350
+ ffi.ffiCall(SetDIBitsToDevice, [
351
+ ffi.FFI_TYPE_UINT64, FFI_S32, FFI_S32, FFI_U32, FFI_U32,
352
+ FFI_S32, FFI_S32, FFI_U32, FFI_U32,
353
+ FFI_PTR, FFI_PTR, FFI_U32
354
+ ], [hdc, 0, 0, pm.w, pm.h, 0, 0, 0, pm.h, pm.data, bmi, 0], FFI_S32);
355
+ ffi.ffiCall(ReleaseDC, [ffi.FFI_TYPE_UINT64, ffi.FFI_TYPE_UINT64], [hwnd, hdc], FFI_S32);
356
+ }
357
+ return 0;
358
+ }
359
+ return gui.DefWindowProc(hwnd, msg, wParam, lParam);
360
+ });
361
+ // 画布消息处理(PAINT 填白、滚动条、滚轮)
362
+ gui.SetWindowProc(hwndCanvas, (hwnd, msg, wParam, lParam) => {
363
+ if (msg === 20 /* gui.WmMsg.ERASEBKGND */)
364
+ return 1;
365
+ if (msg === 15 /* gui.WmMsg.PAINT */) {
366
+ const cr = gui.GetClientRect(hwnd);
367
+ if (cr) {
368
+ const cw = cr.right - cr.left, ch = cr.bottom - cr.top;
369
+ const hdc = ffi.ffiCall(GetDC, [ffi.FFI_TYPE_UINT64], [hwnd], ffi.FFI_TYPE_UINT64);
370
+ if (hdc) {
371
+ ffi.ffiCall(PatBlt, [ffi.FFI_TYPE_UINT64, FFI_S32, FFI_S32, FFI_S32, FFI_S32, FFI_U32], [hdc, 0, 0, cw, ch, WHITENESS], FFI_U32);
372
+ ffi.ffiCall(ReleaseDC, [ffi.FFI_TYPE_UINT64, ffi.FFI_TYPE_UINT64], [hwnd, hdc], FFI_S32);
373
+ }
374
+ }
375
+ gui.DefWindowProc(hwnd, msg, wParam, lParam);
376
+ return 0;
377
+ }
378
+ if (msg === 277 /* gui.WmMsg.VSCROLL */ || msg === 276 /* gui.WmMsg.HSCROLL */) {
379
+ const bar = msg === 277 /* gui.WmMsg.VSCROLL */ ? 1 /* gui.ScrollBar.VERT */ : 0 /* gui.ScrollBar.HORZ */;
380
+ const info = gui.GetScrollInfo(hwnd, bar);
381
+ const maxPos = info.max - info.page + 1;
382
+ const code = wParam & 0xFFFF;
383
+ let newPos = bar === 1 /* gui.ScrollBar.VERT */ ? scrollY : scrollX;
384
+ if (code === 5 /* gui.ScrollCmd.THUMBTRACK */ || code === 4 /* gui.ScrollCmd.THUMBPOSITION */)
385
+ newPos = Math.max(0, Math.min(maxPos, (wParam >> 16) & 0xFFFF));
386
+ else if (code === 0 /* gui.ScrollCmd.LINEUP */)
387
+ newPos -= 20;
388
+ else if (code === 1 /* gui.ScrollCmd.LINEDOWN */)
389
+ newPos += 20;
390
+ else if (code === 2 /* gui.ScrollCmd.PAGEUP */)
391
+ newPos -= info.page;
392
+ else if (code === 3 /* gui.ScrollCmd.PAGEDOWN */)
393
+ newPos += info.page;
394
+ else
395
+ return 0;
396
+ newPos = Math.max(0, Math.min(maxPos, newPos));
397
+ if (bar === 1 /* gui.ScrollBar.VERT */)
398
+ doScroll(0, newPos - scrollY);
399
+ else
400
+ doScroll(newPos - scrollX, 0);
401
+ return 0;
402
+ }
403
+ if (msg === 522 /* gui.WmMsg.MOUSEWHEEL */) {
404
+ const raw = (wParam >>> 16) & 0xFFFF;
405
+ const wheel = raw >= 0x8000 ? raw - 0x10000 : raw;
406
+ const isHorz = (wParam & 4 /* gui.MouseKeyFlag.MK_SHIFT */) !== 0;
407
+ const bar = isHorz ? 0 /* gui.ScrollBar.HORZ */ : 1 /* gui.ScrollBar.VERT */;
408
+ const info = gui.GetScrollInfo(hwnd, bar);
409
+ const maxPos = info.max - info.page + 1;
410
+ let newPos = (isHorz ? scrollX : scrollY) - Math.round(wheel * 40 / 120);
411
+ newPos = Math.max(0, Math.min(maxPos, newPos));
412
+ if (isHorz)
413
+ doScroll(newPos - scrollX, 0);
414
+ else
415
+ doScroll(0, newPos - scrollY);
416
+ return 0;
417
+ }
418
+ if (msg === 5 /* gui.WmMsg.SIZE */) {
419
+ updateScrollRange();
420
+ return gui.DefWindowProc(hwnd, msg, wParam, lParam);
421
+ }
422
+ return gui.DefWindowProc(hwnd, msg, wParam, lParam);
423
+ });
424
+ gui.ShowWindow(hwndMain);
425
+ const test = std.open('example.pdf', 'rb');
426
+ if (test) {
427
+ test.close();
428
+ showPdf(mupdf, 'example.pdf', 0);
429
+ }
430
+ }
431
+ main();