quickwin 2.1.1 → 2.3.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 +9 -9
- package/README.md +9 -9
- package/constants.ts +1063 -0
- package/enum_map.d.ts +129 -0
- package/examples/PdfCanvas.js +2 -2
- package/examples/PdfCanvas.tsx +3 -2
- package/examples/{exec_server.js → execd/main.js} +1114 -287
- package/examples/execd/tray.js +192 -0
- package/examples/execd/worker.js +122 -0
- package/examples/pdf_preview2.js +46 -46
- package/examples/pdf_preview2.ts +55 -54
- package/examples/pdf_viewer.js +134 -130
- package/examples/pdf_viewer.tsx +6 -5
- package/examples/test_gallery.js +135 -131
- package/examples/test_gallery.tsx +9 -8
- package/examples/test_react_lazy.js +130 -128
- package/examples/test_react_lazy.tsx +7 -6
- package/examples/test_react_listbox.js +130 -128
- package/examples/test_react_listbox.tsx +8 -7
- package/examples/test_root_window.js +130 -128
- package/examples/test_root_window.tsx +5 -5
- package/examples/test_tab.js +130 -128
- package/examples/test_tab.tsx +7 -6
- package/examples/tray_demo.js +8 -8
- package/examples/tray_demo.tsx +9 -8
- package/lib/dumpRects.js +2 -2
- package/lib/dumpRects.ts +3 -2
- package/lib/fetch.js +15 -15
- package/lib/fetch.ts +16 -15
- package/lib/ffi/bind.js +3 -3
- package/lib/ffi/bind.ts +3 -3
- package/lib/ffi/ctype.ts +3 -1
- package/lib/ffi/struct.js +23 -7
- package/lib/ffi/struct.ts +50 -23
- package/lib/http-server.js +5 -5
- package/lib/http-server.ts +6 -5
- package/lib/react-qw/components/Button.js +2 -2
- package/lib/react-qw/components/CheckBox.js +4 -4
- package/lib/react-qw/components/ComboBox.js +10 -10
- package/lib/react-qw/components/DateTimePicker.js +12 -12
- package/lib/react-qw/components/Input.js +7 -7
- package/lib/react-qw/components/Link.js +4 -4
- package/lib/react-qw/components/ListBox.js +12 -12
- package/lib/react-qw/components/ListView.js +35 -35
- package/lib/react-qw/components/PathPicker.js +3 -3
- package/lib/react-qw/components/ProgressBar.js +4 -4
- package/lib/react-qw/components/RadioButton.js +4 -4
- package/lib/react-qw/components/ScrollView.js +28 -28
- package/lib/react-qw/components/Slider.js +11 -11
- package/lib/react-qw/components/Tab.js +10 -10
- package/lib/react-qw/components/Tooltip.js +8 -8
- package/lib/react-qw/components/TreeView.js +20 -20
- package/lib/react-qw/index.js +5 -5
- package/lib/react-qw/index.ts +6 -5
- package/lib/react-qw/props.js +4 -4
- package/lib/react-qw/props.ts +5 -4
- package/lib/react-qw/reconciler.js +10 -10
- package/lib/react-qw/reconciler.ts +11 -10
- package/lib/text-measure.js +3 -3
- package/lib/text-measure.ts +4 -3
- package/lib/websocket.js +17 -17
- package/lib/websocket.ts +18 -17
- package/lib/windows/advapi32.js +7 -7
- package/lib/windows/advapi32.ts +7 -7
- package/lib/windows/gdi32.js +17 -17
- package/lib/windows/gdi32.ts +17 -17
- package/lib/windows/kernel32.js +41 -2
- package/lib/windows/kernel32.ts +45 -2
- package/lib/windows/shell32.js +24 -3
- package/lib/windows/shell32.ts +27 -4
- package/lib/windows/structs.js +230 -92
- package/lib/windows/structs.ts +278 -92
- package/lib/windows/user32.js +18 -6
- package/lib/windows/user32.ts +18 -6
- package/lib/windows/winspool.js +3 -3
- package/lib/windows/winspool.ts +3 -3
- package/package.json +4 -3
- package/quickwin.d.ts +32 -10
- 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/test_ffi_bind.js +12 -10
- package/test/test_ffi_bind.ts +14 -12
- package/test/test_ffi_struct.js +60 -26
- package/test/test_ffi_struct.ts +58 -28
- package/test/test_ffi_windows.js +1 -1
- package/test/test_ffi_windows.ts +1 -1
- package/test/test_files.js +7 -7
- package/test/test_files.ts +8 -8
- package/test/test_gdi_draw.js +51 -51
- package/test/test_gdi_draw.ts +57 -57
- package/test/test_http_server.js +3 -3
- package/test/test_http_server.ts +4 -3
- package/test/test_ipv6.js +3 -3
- package/test/test_ipv6.ts +4 -3
- package/test/test_net_event.js +3 -3
- package/test/test_net_event.ts +4 -3
- package/test/test_printer.js +4 -4
- package/test/test_printer.ts +5 -5
- package/test/test_react_complex.js +132 -130
- package/test/test_react_counter.js +130 -128
- package/test/test_react_flex.js +132 -130
- package/test/test_react_hidden.js +130 -128
- package/test/test_react_listview_link.js +131 -129
- package/test/test_react_notify.js +148 -146
- package/test/test_react_render.js +130 -128
- package/test/test_registry.js +6 -6
- package/test/test_registry.ts +8 -8
- package/test/test_wolfssl_read.js +8 -8
- package/test/test_wolfssl_read.ts +9 -8
- package/examples/exec_server.tsx +0 -488
- package/examples/exec_server_worker.js +0 -395
- package/examples/exec_server_worker.ts +0 -417
- package/examples/setres.js +0 -79
- package/examples/setres.ts +0 -86
- package/quickwin_const.d.ts +0 -1037
|
@@ -10,34 +10,34 @@ export const Slider = forwardRef(({ value, onChange, min = 0, max = 100, vertica
|
|
|
10
10
|
if (!h)
|
|
11
11
|
return;
|
|
12
12
|
const lParam = (min & 0xFFFF) | ((max & 0xFFFF) << 16);
|
|
13
|
-
gui.SendMessage(h, 1030 /*
|
|
14
|
-
gui.SendMessage(h, 1029 /*
|
|
13
|
+
gui.SendMessage(h, 1030 /* TbMsg.SETRANGE */, 0, lParam);
|
|
14
|
+
gui.SendMessage(h, 1029 /* TbMsg.SETPOS */, 1, value);
|
|
15
15
|
}, [min, max]);
|
|
16
16
|
useEffect(() => {
|
|
17
17
|
const h = sliderRef.current;
|
|
18
18
|
if (!h)
|
|
19
19
|
return;
|
|
20
|
-
gui.SendMessage(h, 1029 /*
|
|
20
|
+
gui.SendMessage(h, 1029 /* TbMsg.SETPOS */, 1, value);
|
|
21
21
|
}, [value]);
|
|
22
|
-
const tbStyle = 268435456 /*
|
|
23
|
-
| 1 /*
|
|
24
|
-
| (vertical ? 2 /*
|
|
25
|
-
return (_jsx("w", { type: "STATIC", ws: 268435456 /*
|
|
22
|
+
const tbStyle = 268435456 /* WindowStyle.VISIBLE */ | 65536 /* WindowStyle.TABSTOP */
|
|
23
|
+
| 1 /* TrackBarStyle.AUTOTICKS */
|
|
24
|
+
| (vertical ? 2 /* TrackBarStyle.VERT */ : 0);
|
|
25
|
+
return (_jsx("w", { type: "STATIC", ws: 268435456 /* WindowStyle.VISIBLE */ | 33554432 /* WindowStyle.CLIPCHILDREN */, style: style, ref: (h) => {
|
|
26
26
|
wrapperRef.current = h;
|
|
27
27
|
if (typeof ref === 'function')
|
|
28
28
|
ref(h);
|
|
29
29
|
else if (ref)
|
|
30
30
|
ref.current = h;
|
|
31
31
|
}, onEvent: (e) => {
|
|
32
|
-
if (e.msg === 132 /*
|
|
33
|
-
e.msg === 161 /*
|
|
32
|
+
if (e.msg === 132 /* WmMsg.NCHITTEST */ ||
|
|
33
|
+
e.msg === 161 /* WmMsg.NCLBUTTONDOWN */) {
|
|
34
34
|
return DefWindowProc(e.hwnd, e.msg, e.wParam, e.lParam);
|
|
35
35
|
}
|
|
36
|
-
if (e.msg === 276 /*
|
|
36
|
+
if (e.msg === 276 /* WmMsg.HSCROLL */ || e.msg === 277 /* WmMsg.VSCROLL */) {
|
|
37
37
|
const h = sliderRef.current;
|
|
38
38
|
if (!h)
|
|
39
39
|
return 0;
|
|
40
|
-
const pos = gui.SendMessage(h, 1024 /*
|
|
40
|
+
const pos = gui.SendMessage(h, 1024 /* TbMsg.GETPOS */, 0, 0);
|
|
41
41
|
onChange?.(pos);
|
|
42
42
|
return 0;
|
|
43
43
|
}
|
|
@@ -13,11 +13,11 @@ export const Tab = forwardRef(({ tabs, selectedIndex: controlledIndex, defaultSe
|
|
|
13
13
|
const h = tabHwnd.current;
|
|
14
14
|
if (!h)
|
|
15
15
|
return;
|
|
16
|
-
gui.SendMessage(h, 4873 /*
|
|
16
|
+
gui.SendMessage(h, 4873 /* TcMsg.DELETEALLITEMS */, 0, 0);
|
|
17
17
|
for (let i = 0; i < tabs.length; i++) {
|
|
18
18
|
const titleBuf = WCHAR.encode(tabs[i].title);
|
|
19
19
|
const tci = TCITEMW.encode({
|
|
20
|
-
mask: 1 /*
|
|
20
|
+
mask: 1 /* TcItemFlag.TEXT */,
|
|
21
21
|
dwState: 0,
|
|
22
22
|
dwStateMask: 0,
|
|
23
23
|
pszText: titleBuf.ptr,
|
|
@@ -26,25 +26,25 @@ export const Tab = forwardRef(({ tabs, selectedIndex: controlledIndex, defaultSe
|
|
|
26
26
|
lParam: 0,
|
|
27
27
|
});
|
|
28
28
|
const tciPtr = tci.ptr;
|
|
29
|
-
gui.SendMessage(h, 4926 /*
|
|
29
|
+
gui.SendMessage(h, 4926 /* TcMsg.INSERTITEMW */, i, tciPtr);
|
|
30
30
|
}
|
|
31
|
-
gui.SendMessage(h, 4876 /*
|
|
31
|
+
gui.SendMessage(h, 4876 /* TcMsg.SETCURSEL */, sel, 0);
|
|
32
32
|
}, [tabs]);
|
|
33
33
|
useEffect(() => {
|
|
34
34
|
const h = tabHwnd.current;
|
|
35
35
|
if (h)
|
|
36
|
-
gui.SendMessage(h, 4876 /*
|
|
36
|
+
gui.SendMessage(h, 4876 /* TcMsg.SETCURSEL */, sel, 0);
|
|
37
37
|
}, [sel]);
|
|
38
|
-
return (_jsxs("w", { type: "STATIC", ws: 268435456 /*
|
|
39
|
-
if (e.msg === 78 /*
|
|
38
|
+
return (_jsxs("w", { type: "STATIC", ws: 268435456 /* WindowStyle.VISIBLE */ | 33554432 /* WindowStyle.CLIPCHILDREN */, style: { ...style, flexDirection: 'column', alignItems: 'stretch' }, ref: ref, onEvent: (e) => {
|
|
39
|
+
if (e.msg === 78 /* WmMsg.NOTIFY */) {
|
|
40
40
|
const h = tabHwnd.current;
|
|
41
41
|
if (!h)
|
|
42
42
|
return;
|
|
43
43
|
const code = nmCode(e.lParam);
|
|
44
44
|
// SysTabControl32 在 comctl32 v6 下不发标准的 TCN_SELCHANGE (-550),
|
|
45
45
|
// 收到 NM_CLICK (-2) 或 TCN_SELCHANGING (-551) 时读实际选中项
|
|
46
|
-
if (code === -552 /*
|
|
47
|
-
const newSel = gui.SendMessage(h, 4875 /*
|
|
46
|
+
if (code === -552 /* TcNotifyCode.SELCHANGING */ || code === -2 /* SysLinkNotifyCode.CLICK */) {
|
|
47
|
+
const newSel = gui.SendMessage(h, 4875 /* TcMsg.GETCURSEL */, 0, 0);
|
|
48
48
|
if (newSel !== sel) {
|
|
49
49
|
if (!isControlled)
|
|
50
50
|
setInternalIndex(newSel);
|
|
@@ -52,5 +52,5 @@ export const Tab = forwardRef(({ tabs, selectedIndex: controlledIndex, defaultSe
|
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
|
-
}, children: [_jsx("w", { type: "SysTabControl32", ws: 268435456 /*
|
|
55
|
+
}, children: [_jsx("w", { type: "SysTabControl32", ws: 268435456 /* WindowStyle.VISIBLE */ | 32768 /* TabStyle.FOCUSNEVER */, style: { height: 20 }, ref: tabHwnd }), _jsx("w", { type: "STATIC", ws: 268435456 /* WindowStyle.VISIBLE */ | 33554432 /* WindowStyle.CLIPCHILDREN */, style: { flexGrow: 1, flexDirection: 'column', alignItems: 'stretch' }, children: tabs[sel]?.content })] }));
|
|
56
56
|
});
|
|
@@ -9,7 +9,7 @@ function buildToolInfo(hTarget, text) {
|
|
|
9
9
|
const buf = new PtrArrayBuffer(size + textLen);
|
|
10
10
|
TTTOOLINFOW.encode({
|
|
11
11
|
cbSize: size,
|
|
12
|
-
uFlags: 16 /*
|
|
12
|
+
uFlags: 16 /* TtToolFlag.SUBCLASS */ | 1 /* TtToolFlag.IDISHWND */,
|
|
13
13
|
hwnd: hTarget,
|
|
14
14
|
uId: hTarget,
|
|
15
15
|
rect: [0, 0, 0, 0],
|
|
@@ -31,20 +31,20 @@ function Tooltip({ text, children, balloon }) {
|
|
|
31
31
|
const hTarget = childRef.current;
|
|
32
32
|
if (!hTarget)
|
|
33
33
|
return;
|
|
34
|
-
const hTT = gui.CreateWindow('tooltips_class32', '', -2147483648 /*
|
|
35
|
-
| (balloon ? 64 /*
|
|
34
|
+
const hTT = gui.CreateWindow('tooltips_class32', '', -2147483648 /* WindowStyle.POPUP */ | 1 /* TooltipStyle.ALWAYSTIP */ | 2 /* TooltipStyle.NOPREFIX */
|
|
35
|
+
| (balloon ? 64 /* TooltipStyle.BALLOON */ : 0), 0, 0, 0, 0, hTarget, null);
|
|
36
36
|
if (!hTT)
|
|
37
37
|
return;
|
|
38
38
|
hTTRef.current = hTT;
|
|
39
|
-
SetWindowPos(hTT, -1 /*
|
|
39
|
+
SetWindowPos(hTT, -1 /* SetWindowPosHwnd.TOPMOST */, 0, 0, 0, 0, 2 /* SetWindowPosFlag.SWP_NOMOVE */ | 1 /* SetWindowPosFlag.SWP_NOSIZE */ | 16 /* SetWindowPosFlag.SWP_NOACTIVATE */);
|
|
40
40
|
const ti = buildToolInfo(hTarget, text);
|
|
41
|
-
gui.SendMessage(hTT, 1074 /*
|
|
42
|
-
gui.SendMessage(hTT, 1048 /*
|
|
43
|
-
gui.SendMessage(hTT, 1025 /*
|
|
41
|
+
gui.SendMessage(hTT, 1074 /* TtMsg.ADDTOOLW */, 0, ti.ptr);
|
|
42
|
+
gui.SendMessage(hTT, 1048 /* TtMsg.SETMAXTIPWIDTH */, 0, 400);
|
|
43
|
+
gui.SendMessage(hTT, 1025 /* TtMsg.ACTIVATE */, 1, 0);
|
|
44
44
|
return () => {
|
|
45
45
|
if (hTT) {
|
|
46
46
|
const ti2 = buildToolInfo(hTarget, text);
|
|
47
|
-
gui.SendMessage(hTT, 1075 /*
|
|
47
|
+
gui.SendMessage(hTT, 1075 /* TtMsg.DELTOOLW */, 0, ti2.ptr);
|
|
48
48
|
gui.DestroyWindow(hTT);
|
|
49
49
|
}
|
|
50
50
|
hTTRef.current = null;
|
|
@@ -5,9 +5,9 @@ import { WCHAR } from '../../ffi/bind.js';
|
|
|
5
5
|
import { TVITEM, TVINSERTSTRUCT } from '../../windows/structs.js';
|
|
6
6
|
import { nmCode } from '../nmhdr.js';
|
|
7
7
|
function buildTvItem(textPtr, cChildren) {
|
|
8
|
-
let mask = 1 /*
|
|
8
|
+
let mask = 1 /* TvIfFlag.TEXT */;
|
|
9
9
|
if (cChildren > 0)
|
|
10
|
-
mask |= 64 /*
|
|
10
|
+
mask |= 64 /* TvIfFlag.CHILDREN */;
|
|
11
11
|
return TVITEM.encode({
|
|
12
12
|
mask, hItem: 0, state: 0, stateMask: 0,
|
|
13
13
|
pszText: textPtr, cchTextMax: 260,
|
|
@@ -20,21 +20,21 @@ function insertItems(hTree, nodes, parentHandle, hItemMap, keyMap) {
|
|
|
20
20
|
const textBuf = WCHAR.encode(node.label);
|
|
21
21
|
const itemBuf = buildTvItem(textBuf.ptr, cChildren);
|
|
22
22
|
const item = TVITEM.decode(itemBuf);
|
|
23
|
-
const tvins = TVINSERTSTRUCT.encode({ hParent: parentHandle, hInsertAfter: -65536 /*
|
|
24
|
-
const hItem = gui.SendMessage(hTree, 4402 /*
|
|
23
|
+
const tvins = TVINSERTSTRUCT.encode({ hParent: parentHandle, hInsertAfter: -65536 /* TvInsertAfter.ROOT */, item });
|
|
24
|
+
const hItem = gui.SendMessage(hTree, 4402 /* TvMsg.INSERTITEMW */, 0, tvins.ptr);
|
|
25
25
|
hItemMap.set(hItem, node);
|
|
26
26
|
if (node.key)
|
|
27
27
|
keyMap.set(node.key, hItem);
|
|
28
28
|
if (node.children && node.children.length > 0) {
|
|
29
29
|
insertItems(hTree, node.children, hItem, hItemMap, keyMap);
|
|
30
|
-
gui.SendMessage(hTree, 4354 /*
|
|
30
|
+
gui.SendMessage(hTree, 4354 /* TvMsg.EXPAND */, 2 /* TvExpandCmd.EXPAND */, hItem);
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
function deleteAllItems(hTree) {
|
|
35
|
-
const n = gui.SendMessage(hTree, 4357 /*
|
|
35
|
+
const n = gui.SendMessage(hTree, 4357 /* TvMsg.GETCOUNT */, 0, 0);
|
|
36
36
|
if (n > 0)
|
|
37
|
-
gui.SendMessage(hTree, 4353 /*
|
|
37
|
+
gui.SendMessage(hTree, 4353 /* TvMsg.DELETEITEM */, 1, -65536 /* TvInsertAfter.ROOT */);
|
|
38
38
|
}
|
|
39
39
|
const TreeView = forwardRef(function TreeViewInner({ data, onSelect, defaultSelectedKey, selectedKey: controlledKey, style }, ref) {
|
|
40
40
|
const [internalKey, setInternalKey] = useState(defaultSelectedKey ?? null);
|
|
@@ -45,12 +45,12 @@ const TreeView = forwardRef(function TreeViewInner({ data, onSelect, defaultSele
|
|
|
45
45
|
const keyMapRef = useRef(new Map());
|
|
46
46
|
const onChangeRef = useRef(onSelect);
|
|
47
47
|
onChangeRef.current = onSelect;
|
|
48
|
-
const tvWs = 268435456 /*
|
|
49
|
-
| 65536 /*
|
|
50
|
-
| 2 /*
|
|
51
|
-
| 1 /*
|
|
52
|
-
| 4 /*
|
|
53
|
-
| 32 /*
|
|
48
|
+
const tvWs = 268435456 /* WindowStyle.VISIBLE */ | 8388608 /* WindowStyle.BORDER */ | 2097152 /* WindowStyle.VSCROLL */
|
|
49
|
+
| 65536 /* WindowStyle.TABSTOP */
|
|
50
|
+
| 2 /* TreeViewStyle.HASLINES */
|
|
51
|
+
| 1 /* TreeViewStyle.HASBUTTONS */
|
|
52
|
+
| 4 /* TreeViewStyle.LINESATROOT */
|
|
53
|
+
| 32 /* TreeViewStyle.SHOWSELALWAYS */;
|
|
54
54
|
useEffect(() => {
|
|
55
55
|
const h = tvRef.current;
|
|
56
56
|
if (!h)
|
|
@@ -59,13 +59,13 @@ const TreeView = forwardRef(function TreeViewInner({ data, onSelect, defaultSele
|
|
|
59
59
|
hItemMapRef.current.clear();
|
|
60
60
|
keyMapRef.current.clear();
|
|
61
61
|
if (data && data.length > 0)
|
|
62
|
-
insertItems(h, data, -65536 /*
|
|
62
|
+
insertItems(h, data, -65536 /* TvInsertAfter.ROOT */, hItemMapRef.current, keyMapRef.current);
|
|
63
63
|
}, [data]);
|
|
64
64
|
useEffect(() => {
|
|
65
65
|
const h = tvRef.current;
|
|
66
66
|
if (!h)
|
|
67
67
|
return;
|
|
68
|
-
gui.SendMessage(h, 4396 /*
|
|
68
|
+
gui.SendMessage(h, 4396 /* TvMsg.SETEXTENDEDSTYLE */, 0, 4 /* TvExStyle.DOUBLEBUFFER */);
|
|
69
69
|
}, []);
|
|
70
70
|
useEffect(() => {
|
|
71
71
|
if (selKey == null)
|
|
@@ -75,16 +75,16 @@ const TreeView = forwardRef(function TreeViewInner({ data, onSelect, defaultSele
|
|
|
75
75
|
return;
|
|
76
76
|
const hItem = keyMapRef.current.get(selKey);
|
|
77
77
|
if (hItem !== undefined)
|
|
78
|
-
gui.SendMessage(h, 4363 /*
|
|
78
|
+
gui.SendMessage(h, 4363 /* TvMsg.SELECTITEM */, 9 /* TvGnRelative.CARET */, hItem);
|
|
79
79
|
}, [selKey]);
|
|
80
|
-
return (_jsx("w", { type: "STATIC", ws: 268435456 /*
|
|
81
|
-
if (e.msg === 78 /*
|
|
80
|
+
return (_jsx("w", { type: "STATIC", ws: 268435456 /* WindowStyle.VISIBLE */ | 33554432 /* WindowStyle.CLIPCHILDREN */, style: { ...style, flexDirection: 'column', alignItems: 'stretch' }, ref: ref, onEvent: (e) => {
|
|
81
|
+
if (e.msg === 78 /* WmMsg.NOTIFY */) {
|
|
82
82
|
const code = nmCode(e.lParam);
|
|
83
|
-
if (code === -451 /*
|
|
83
|
+
if (code === -451 /* TvNotifyCode.SELCHANGEDW */) {
|
|
84
84
|
const h = tvRef.current;
|
|
85
85
|
if (!h)
|
|
86
86
|
return;
|
|
87
|
-
const hCaret = gui.SendMessage(h, 4362 /*
|
|
87
|
+
const hCaret = gui.SendMessage(h, 4362 /* TvMsg.GETNEXTITEM */, 9 /* TvGnRelative.CARET */, 0);
|
|
88
88
|
const node = hItemMapRef.current.get(hCaret) ?? null;
|
|
89
89
|
const key = node?.key ?? null;
|
|
90
90
|
if (!isControlled)
|
package/lib/react-qw/index.js
CHANGED
|
@@ -10,7 +10,7 @@ function ensureDefaultClass() {
|
|
|
10
10
|
return DEFAULT_CLASS;
|
|
11
11
|
gui.RegisterClass(DEFAULT_CLASS, (hwnd, msg, wParam, lParam) => {
|
|
12
12
|
const entry = rootMap.get(hwnd);
|
|
13
|
-
if (msg === 2 /*
|
|
13
|
+
if (msg === 2 /* WmMsg.DESTROY */ && entry) {
|
|
14
14
|
rootMap.delete(hwnd);
|
|
15
15
|
reconciler.updateContainer(null, entry.root, null, () => {
|
|
16
16
|
instancesByHwnd.delete(hwnd);
|
|
@@ -19,7 +19,7 @@ function ensureDefaultClass() {
|
|
|
19
19
|
const result = entry?.onEvent?.({ hwnd, msg, wParam, lParam });
|
|
20
20
|
if (typeof result === 'number')
|
|
21
21
|
return result;
|
|
22
|
-
if (msg === 5 /*
|
|
22
|
+
if (msg === 5 /* WmMsg.SIZE */)
|
|
23
23
|
forceFlexLayout(hwnd);
|
|
24
24
|
return DefWindowProc(hwnd, msg, wParam, lParam);
|
|
25
25
|
});
|
|
@@ -31,15 +31,15 @@ export function createRoot(container) {
|
|
|
31
31
|
if (typeof container === 'object') {
|
|
32
32
|
const cfg = container;
|
|
33
33
|
const className = ensureDefaultClass();
|
|
34
|
-
const ws = (cfg.ws ?? 0) | 13565952 /*
|
|
34
|
+
const ws = (cfg.ws ?? 0) | 13565952 /* WindowStyle.OVERLAPPEDWINDOW */;
|
|
35
35
|
const w = (cfg.width ?? 800) * scaleFactor;
|
|
36
36
|
const h = (cfg.height ?? 600) * scaleFactor;
|
|
37
37
|
// GetScreenSize 等价内联:SM_CXSCREEN=0 / SM_CYSCREEN=1
|
|
38
38
|
const [sw, sh] = [GetSystemMetrics(0), GetSystemMetrics(1)];
|
|
39
39
|
const x = cfg.x === undefined ? ((sw - w) / 2) | 0
|
|
40
|
-
: cfg.x === -2147483648 /*
|
|
40
|
+
: cfg.x === -2147483648 /* CreatePos.USEDEFAULT */ ? -2147483648 /* CreatePos.USEDEFAULT */ : cfg.x * scaleFactor;
|
|
41
41
|
const y = cfg.y === undefined ? ((sh - h) / 2) | 0
|
|
42
|
-
: cfg.y === -2147483648 /*
|
|
42
|
+
: cfg.y === -2147483648 /* CreatePos.USEDEFAULT */ ? -2147483648 /* CreatePos.USEDEFAULT */ : cfg.y * scaleFactor;
|
|
43
43
|
const hwin = gui.CreateWindow(className, cfg.text || '', ws, x, y, w, h, null, null);
|
|
44
44
|
if (!hwin)
|
|
45
45
|
throw new Error('CreateWindow failed');
|
package/lib/react-qw/index.ts
CHANGED
|
@@ -2,6 +2,7 @@ import * as gui from 'gui'
|
|
|
2
2
|
import React from 'react'
|
|
3
3
|
import { DefWindowProc, GetSystemMetrics } from '../windows/user32.js'
|
|
4
4
|
import reconciler, { scaleFactor, instancesByHwnd, forceFlexLayout, type Instance } from './reconciler.js'
|
|
5
|
+
import { CreatePos, WindowStyle, WmMsg } from '../../constants'
|
|
5
6
|
|
|
6
7
|
const noop = () => { }
|
|
7
8
|
|
|
@@ -24,7 +25,7 @@ function ensureDefaultClass() {
|
|
|
24
25
|
if (defaultClassRegistered) return DEFAULT_CLASS
|
|
25
26
|
gui.RegisterClass(DEFAULT_CLASS, (hwnd, msg, wParam, lParam) => {
|
|
26
27
|
const entry = rootMap.get(hwnd)
|
|
27
|
-
if (msg ===
|
|
28
|
+
if (msg === WmMsg.DESTROY && entry) {
|
|
28
29
|
rootMap.delete(hwnd)
|
|
29
30
|
reconciler.updateContainer(null, entry.root, null, () => {
|
|
30
31
|
instancesByHwnd.delete(hwnd)
|
|
@@ -32,7 +33,7 @@ function ensureDefaultClass() {
|
|
|
32
33
|
}
|
|
33
34
|
const result = entry?.onEvent?.({ hwnd, msg, wParam, lParam })
|
|
34
35
|
if (typeof result === 'number') return result
|
|
35
|
-
if (msg ===
|
|
36
|
+
if (msg === WmMsg.SIZE) forceFlexLayout(hwnd)
|
|
36
37
|
return DefWindowProc(hwnd, msg, wParam, lParam)
|
|
37
38
|
})
|
|
38
39
|
defaultClassRegistered = true
|
|
@@ -44,16 +45,16 @@ export function createRoot(container: gui.HWND | RootWindowConfig) {
|
|
|
44
45
|
if (typeof container === 'object') {
|
|
45
46
|
const cfg = container
|
|
46
47
|
const className = ensureDefaultClass()
|
|
47
|
-
const ws = (cfg.ws ?? 0) |
|
|
48
|
+
const ws = (cfg.ws ?? 0) | WindowStyle.OVERLAPPEDWINDOW
|
|
48
49
|
const w = (cfg.width ?? 800) * scaleFactor
|
|
49
50
|
const h = (cfg.height ?? 600) * scaleFactor
|
|
50
51
|
// GetScreenSize 等价内联:SM_CXSCREEN=0 / SM_CYSCREEN=1
|
|
51
52
|
const [sw, sh] = [GetSystemMetrics(0), GetSystemMetrics(1)]
|
|
52
53
|
const x = cfg.x === undefined ? ((sw - w) / 2) | 0
|
|
53
|
-
: cfg.x ===
|
|
54
|
+
: cfg.x === CreatePos.USEDEFAULT ? CreatePos.USEDEFAULT
|
|
54
55
|
: cfg.x * scaleFactor
|
|
55
56
|
const y = cfg.y === undefined ? ((sh - h) / 2) | 0
|
|
56
|
-
: cfg.y ===
|
|
57
|
+
: cfg.y === CreatePos.USEDEFAULT ? CreatePos.USEDEFAULT
|
|
57
58
|
: cfg.y * scaleFactor
|
|
58
59
|
const hwin = gui.CreateWindow(className, cfg.text || '', ws, x, y, w, h, null, null)
|
|
59
60
|
if (!hwin) throw new Error('CreateWindow failed')
|
package/lib/react-qw/props.js
CHANGED
|
@@ -12,21 +12,21 @@ export function applyProps(instance, newProps, _oldProps) {
|
|
|
12
12
|
if (textVal !== undefined) {
|
|
13
13
|
let cursor = -1;
|
|
14
14
|
if (instance.type === 'EDIT') {
|
|
15
|
-
const sel = gui.SendMessage(hwnd, 176 /*
|
|
15
|
+
const sel = gui.SendMessage(hwnd, 176 /* EditMsg.GETSEL */, 0, 0);
|
|
16
16
|
cursor = sel >>> 16;
|
|
17
17
|
}
|
|
18
18
|
SetWindowText(hwnd, textVal);
|
|
19
19
|
if (instance.type === 'EDIT' && cursor >= 0) {
|
|
20
|
-
gui.SendMessage(hwnd, 177 /*
|
|
20
|
+
gui.SendMessage(hwnd, 177 /* EditMsg.SETSEL */, cursor, cursor);
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
if ('disabled' in newProps) {
|
|
24
24
|
EnableWindow(hwnd, !newProps.disabled ? 1 : 0);
|
|
25
25
|
}
|
|
26
26
|
if ('hidden' in newProps) {
|
|
27
|
-
gui.ShowWindow(hwnd, newProps.hidden ? 0 /*
|
|
27
|
+
gui.ShowWindow(hwnd, newProps.hidden ? 0 /* ShowWindowCmd.HIDE */ : 5 /* ShowWindowCmd.SHOW */);
|
|
28
28
|
}
|
|
29
29
|
if ('visible' in newProps) {
|
|
30
|
-
gui.ShowWindow(hwnd, newProps.visible ? 5 /*
|
|
30
|
+
gui.ShowWindow(hwnd, newProps.visible ? 5 /* ShowWindowCmd.SHOW */ : 0 /* ShowWindowCmd.HIDE */);
|
|
31
31
|
}
|
|
32
32
|
}
|
package/lib/react-qw/props.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as gui from 'gui'
|
|
2
2
|
import { SetWindowText, EnableWindow } from '../windows/user32.js'
|
|
3
3
|
import type { Instance, Props } from './reconciler.js'
|
|
4
|
+
import { EditMsg, ShowWindowCmd } from '../../constants'
|
|
4
5
|
|
|
5
6
|
export function applyProps(
|
|
6
7
|
instance: Instance,
|
|
@@ -20,21 +21,21 @@ export function applyProps(
|
|
|
20
21
|
if (textVal !== undefined) {
|
|
21
22
|
let cursor = -1
|
|
22
23
|
if (instance.type === 'EDIT') {
|
|
23
|
-
const sel = gui.SendMessage(hwnd,
|
|
24
|
+
const sel = gui.SendMessage(hwnd, EditMsg.GETSEL, 0, 0)
|
|
24
25
|
cursor = sel >>> 16
|
|
25
26
|
}
|
|
26
27
|
SetWindowText(hwnd, textVal)
|
|
27
28
|
if (instance.type === 'EDIT' && cursor >= 0) {
|
|
28
|
-
gui.SendMessage(hwnd,
|
|
29
|
+
gui.SendMessage(hwnd, EditMsg.SETSEL, cursor, cursor)
|
|
29
30
|
}
|
|
30
31
|
}
|
|
31
32
|
if ('disabled' in newProps) {
|
|
32
33
|
EnableWindow(hwnd, !newProps.disabled ? 1 : 0)
|
|
33
34
|
}
|
|
34
35
|
if ('hidden' in newProps) {
|
|
35
|
-
gui.ShowWindow(hwnd, newProps.hidden ?
|
|
36
|
+
gui.ShowWindow(hwnd, newProps.hidden ? ShowWindowCmd.HIDE : ShowWindowCmd.SHOW)
|
|
36
37
|
}
|
|
37
38
|
if ('visible' in newProps) {
|
|
38
|
-
gui.ShowWindow(hwnd, newProps.visible ?
|
|
39
|
+
gui.ShowWindow(hwnd, newProps.visible ? ShowWindowCmd.SHOW : ShowWindowCmd.HIDE)
|
|
39
40
|
}
|
|
40
41
|
}
|
|
@@ -24,7 +24,7 @@ function isDelayedControl(winClass) {
|
|
|
24
24
|
}
|
|
25
25
|
function setupWindowProc(instance, hwnd) {
|
|
26
26
|
instance.hwnd = hwnd;
|
|
27
|
-
const oldProc = GetWindowLongPtr(hwnd, -4 /*
|
|
27
|
+
const oldProc = GetWindowLongPtr(hwnd, -4 /* Gwlp.WNDPROC */);
|
|
28
28
|
instancesByHwnd.set(hwnd, instance);
|
|
29
29
|
gui.SetWindowProc(hwnd, (hwnd, msg, wParam, lParam) => {
|
|
30
30
|
const ev = instance.props.onEvent;
|
|
@@ -42,10 +42,10 @@ function ensureChildWindow(child, parentHwnd) {
|
|
|
42
42
|
if (child.hwnd !== null)
|
|
43
43
|
return child.hwnd;
|
|
44
44
|
const sty = child.props.style || {};
|
|
45
|
-
const ws = ((child.props.ws ?? 0) | 1073741824 /*
|
|
45
|
+
const ws = ((child.props.ws ?? 0) | 1073741824 /* WindowStyle.CHILD */) & ~268435456 /* WindowStyle.VISIBLE */;
|
|
46
46
|
const hwnd = gui.CreateWindow(child.type, child.props.text || '', ws, 0, 0, (typeof sty.width === 'number' ? sty.width : 100) * scaleFactor, (typeof sty.height === 'number' ? sty.height : 30) * scaleFactor, parentHwnd, null);
|
|
47
47
|
if (dpiFont)
|
|
48
|
-
gui.SendMessage(hwnd, 48 /*
|
|
48
|
+
gui.SendMessage(hwnd, 48 /* WmMsg.SETFONT */, dpiFont, 1);
|
|
49
49
|
setupWindowProc(child, hwnd);
|
|
50
50
|
return hwnd;
|
|
51
51
|
}
|
|
@@ -104,7 +104,7 @@ function runFlexLayout(inst) {
|
|
|
104
104
|
if (lr && lr.x === r.x && lr.y === r.y && lr.w === r.width && lr.h === r.height)
|
|
105
105
|
continue;
|
|
106
106
|
console.log('flex: set', child.type, child.hwnd, 'to', r.x, r.y, r.width, r.height);
|
|
107
|
-
SetWindowPos(child.hwnd, 0 /*
|
|
107
|
+
SetWindowPos(child.hwnd, 0 /* SetWindowPosHwnd.TOP */, (r.x + pl) * scaleFactor, (r.y + pt) * scaleFactor, r.width * scaleFactor, r.height * scaleFactor, 64 /* SetWindowPosFlag.SWP_SHOWWINDOW */);
|
|
108
108
|
InvalidateRect(child.hwnd, NULL, 1);
|
|
109
109
|
child.lastRect = { x: r.x, y: r.y, w: r.width, h: r.height };
|
|
110
110
|
}
|
|
@@ -133,12 +133,12 @@ const hostConfig = {
|
|
|
133
133
|
}
|
|
134
134
|
const sty = props.style || {};
|
|
135
135
|
// reconciler 创建的都是子窗口,确保 WS_CHILD 避免定位异常;创建时不带 WS_VISIBLE 防止 (0,0) 闪烁
|
|
136
|
-
const ws = ((props.ws ?? 0) | 1073741824 /*
|
|
136
|
+
const ws = ((props.ws ?? 0) | 1073741824 /* WindowStyle.CHILD */) & ~268435456 /* WindowStyle.VISIBLE */;
|
|
137
137
|
if (DEBUG)
|
|
138
138
|
console.log('[reconciler] CreateWindow args:', winClass, props.text ?? '', ws, 0, 0, sty.width ?? 100, sty.height ?? 30, rootContainer);
|
|
139
139
|
const hwnd = gui.CreateWindow(winClass, props.text ?? '', ws, 0, 0, (typeof sty.width === 'number' ? sty.width : 100) * scaleFactor, (typeof sty.height === 'number' ? sty.height : 30) * scaleFactor, rootContainer, null);
|
|
140
140
|
if (dpiFont)
|
|
141
|
-
gui.SendMessage(hwnd, 48 /*
|
|
141
|
+
gui.SendMessage(hwnd, 48 /* WmMsg.SETFONT */, dpiFont, 1);
|
|
142
142
|
if (DEBUG)
|
|
143
143
|
console.log('[reconciler] createInstance hwnd result:', hwnd, 'null?', hwnd === null);
|
|
144
144
|
const instance = { hwnd, type: winClass, props, children: [] };
|
|
@@ -260,19 +260,19 @@ const hostConfig = {
|
|
|
260
260
|
},
|
|
261
261
|
hideInstance(instance) {
|
|
262
262
|
if (instance.hwnd !== null)
|
|
263
|
-
gui.ShowWindow(instance.hwnd, 0 /*
|
|
263
|
+
gui.ShowWindow(instance.hwnd, 0 /* ShowWindowCmd.HIDE */);
|
|
264
264
|
},
|
|
265
265
|
hideTextInstance(instance) {
|
|
266
266
|
if (instance.hwnd !== null)
|
|
267
|
-
gui.ShowWindow(instance.hwnd, 0 /*
|
|
267
|
+
gui.ShowWindow(instance.hwnd, 0 /* ShowWindowCmd.HIDE */);
|
|
268
268
|
},
|
|
269
269
|
unhideInstance(instance, _props) {
|
|
270
270
|
if (instance.hwnd !== null)
|
|
271
|
-
gui.ShowWindow(instance.hwnd, 5 /*
|
|
271
|
+
gui.ShowWindow(instance.hwnd, 5 /* ShowWindowCmd.SHOW */);
|
|
272
272
|
},
|
|
273
273
|
unhideTextInstance(instance, _text) {
|
|
274
274
|
if (instance.hwnd !== null)
|
|
275
|
-
gui.ShowWindow(instance.hwnd, 5 /*
|
|
275
|
+
gui.ShowWindow(instance.hwnd, 5 /* ShowWindowCmd.SHOW */);
|
|
276
276
|
},
|
|
277
277
|
shouldSetTextContent(_type, props) {
|
|
278
278
|
return typeof props.children === 'string' || typeof props.children === 'number';
|
|
@@ -10,6 +10,7 @@ import { applyProps } from './props.js'
|
|
|
10
10
|
import { calculateFlexLayout, type FlexStyle } from './layout.js'
|
|
11
11
|
import { getButtonIdealSize, measureTextForHwnd } from '../text-measure.js'
|
|
12
12
|
import type { WIntrinsicProps } from './jsx.js'
|
|
13
|
+
import { Gwlp, SetWindowPosFlag, SetWindowPosHwnd, ShowWindowCmd, WindowStyle, WmMsg } from '../../constants'
|
|
13
14
|
|
|
14
15
|
// DEBUG 由 esbuild --define 在 bundle 时替换(见 build.ts),生产环境为 false
|
|
15
16
|
declare const DEBUG: boolean
|
|
@@ -42,7 +43,7 @@ function isDelayedControl(winClass: string): boolean {
|
|
|
42
43
|
|
|
43
44
|
function setupWindowProc(instance: Instance, hwnd: gui.HWND) {
|
|
44
45
|
instance.hwnd = hwnd
|
|
45
|
-
const oldProc = GetWindowLongPtr(hwnd,
|
|
46
|
+
const oldProc = GetWindowLongPtr(hwnd, Gwlp.WNDPROC)
|
|
46
47
|
instancesByHwnd.set(hwnd, instance)
|
|
47
48
|
gui.SetWindowProc(hwnd, (hwnd: gui.HWND, msg: number, wParam: number, lParam: number) => {
|
|
48
49
|
const ev = instance.props.onEvent
|
|
@@ -59,14 +60,14 @@ function setupWindowProc(instance: Instance, hwnd: gui.HWND) {
|
|
|
59
60
|
function ensureChildWindow(child: Instance, parentHwnd: gui.HWND): gui.HWND {
|
|
60
61
|
if (child.hwnd !== null) return child.hwnd
|
|
61
62
|
const sty = child.props.style || {}
|
|
62
|
-
const ws = ((child.props.ws ?? 0) |
|
|
63
|
+
const ws = ((child.props.ws ?? 0) | WindowStyle.CHILD) & ~WindowStyle.VISIBLE
|
|
63
64
|
const hwnd = gui.CreateWindow(
|
|
64
65
|
child.type, child.props.text || '', ws,
|
|
65
66
|
0, 0,
|
|
66
67
|
(typeof sty.width === 'number' ? sty.width : 100) * scaleFactor, (typeof sty.height === 'number' ? sty.height : 30) * scaleFactor,
|
|
67
68
|
parentHwnd, null
|
|
68
69
|
)!
|
|
69
|
-
if (dpiFont) gui.SendMessage(hwnd,
|
|
70
|
+
if (dpiFont) gui.SendMessage(hwnd, WmMsg.SETFONT, dpiFont, 1)
|
|
70
71
|
setupWindowProc(child, hwnd)
|
|
71
72
|
return hwnd
|
|
72
73
|
}
|
|
@@ -118,7 +119,7 @@ function runFlexLayout(inst: Instance) {
|
|
|
118
119
|
const lr = child.lastRect
|
|
119
120
|
if (lr && lr.x === r.x && lr.y === r.y && lr.w === r.width && lr.h === r.height) continue
|
|
120
121
|
console.log('flex: set', child.type, child.hwnd, 'to', r.x, r.y, r.width, r.height)
|
|
121
|
-
SetWindowPos(child.hwnd!,
|
|
122
|
+
SetWindowPos(child.hwnd!, SetWindowPosHwnd.TOP, (r.x + pl) * scaleFactor, (r.y + pt) * scaleFactor, r.width * scaleFactor, r.height * scaleFactor, SetWindowPosFlag.SWP_SHOWWINDOW)
|
|
122
123
|
InvalidateRect(child.hwnd!, NULL, 1)
|
|
123
124
|
child.lastRect = { x: r.x, y: r.y, w: r.width, h: r.height }
|
|
124
125
|
}
|
|
@@ -164,7 +165,7 @@ const hostConfig: QuickWinHostConfig = {
|
|
|
164
165
|
}
|
|
165
166
|
const sty = props.style || {}
|
|
166
167
|
// reconciler 创建的都是子窗口,确保 WS_CHILD 避免定位异常;创建时不带 WS_VISIBLE 防止 (0,0) 闪烁
|
|
167
|
-
const ws = ((props.ws ?? 0) |
|
|
168
|
+
const ws = ((props.ws ?? 0) | WindowStyle.CHILD) & ~WindowStyle.VISIBLE
|
|
168
169
|
if (DEBUG) console.log('[reconciler] CreateWindow args:', winClass, props.text ?? '', ws, 0, 0, sty.width ?? 100, sty.height ?? 30, rootContainer)
|
|
169
170
|
const hwnd = gui.CreateWindow(
|
|
170
171
|
winClass, props.text ?? '', ws,
|
|
@@ -172,7 +173,7 @@ const hostConfig: QuickWinHostConfig = {
|
|
|
172
173
|
(typeof sty.width === 'number' ? sty.width : 100) * scaleFactor, (typeof sty.height === 'number' ? sty.height : 30) * scaleFactor,
|
|
173
174
|
rootContainer, null
|
|
174
175
|
)!
|
|
175
|
-
if (dpiFont) gui.SendMessage(hwnd,
|
|
176
|
+
if (dpiFont) gui.SendMessage(hwnd, WmMsg.SETFONT, dpiFont, 1)
|
|
176
177
|
if (DEBUG) console.log('[reconciler] createInstance hwnd result:', hwnd, 'null?', hwnd === null)
|
|
177
178
|
const instance: Instance = { hwnd, type: winClass, props, children: [] }
|
|
178
179
|
setupWindowProc(instance, hwnd)
|
|
@@ -283,19 +284,19 @@ const hostConfig: QuickWinHostConfig = {
|
|
|
283
284
|
},
|
|
284
285
|
|
|
285
286
|
hideInstance(instance: Instance) {
|
|
286
|
-
if (instance.hwnd !== null) gui.ShowWindow(instance.hwnd,
|
|
287
|
+
if (instance.hwnd !== null) gui.ShowWindow(instance.hwnd, ShowWindowCmd.HIDE)
|
|
287
288
|
},
|
|
288
289
|
|
|
289
290
|
hideTextInstance(instance: Instance) {
|
|
290
|
-
if (instance.hwnd !== null) gui.ShowWindow(instance.hwnd,
|
|
291
|
+
if (instance.hwnd !== null) gui.ShowWindow(instance.hwnd, ShowWindowCmd.HIDE)
|
|
291
292
|
},
|
|
292
293
|
|
|
293
294
|
unhideInstance(instance: Instance, _props: Props) {
|
|
294
|
-
if (instance.hwnd !== null) gui.ShowWindow(instance.hwnd,
|
|
295
|
+
if (instance.hwnd !== null) gui.ShowWindow(instance.hwnd, ShowWindowCmd.SHOW)
|
|
295
296
|
},
|
|
296
297
|
|
|
297
298
|
unhideTextInstance(instance: Instance, _text: string) {
|
|
298
|
-
if (instance.hwnd !== null) gui.ShowWindow(instance.hwnd,
|
|
299
|
+
if (instance.hwnd !== null) gui.ShowWindow(instance.hwnd, ShowWindowCmd.SHOW)
|
|
299
300
|
},
|
|
300
301
|
|
|
301
302
|
shouldSetTextContent(_type: string, props: Props) {
|
package/lib/text-measure.js
CHANGED
|
@@ -7,14 +7,14 @@ export function measureText(hdc, text, maxWidth) {
|
|
|
7
7
|
// rect 双向位不注册 encoder(CALCRECT 就地写回):encode(初值) 新建 + DeepPartial 初值一步到位
|
|
8
8
|
//(left/top/bottom 缺省跳过保持 0)→ 传 .ptr → decode 读回
|
|
9
9
|
const r = RECT.encode({ right: maxWidth });
|
|
10
|
-
DrawText(hdc, text, -1, r.ptr, 1024 /*
|
|
10
|
+
DrawText(hdc, text, -1, r.ptr, 1024 /* DrawTextFlag.CALCRECT */);
|
|
11
11
|
const { right, bottom } = RECT.decode(r);
|
|
12
12
|
return { width: right, height: bottom };
|
|
13
13
|
}
|
|
14
14
|
export function getButtonIdealSize(hwnd) {
|
|
15
15
|
// SIZE 纯出参位(GETIDEALSIZE 就地写回):alloc → 传 .ptr → decode 读回
|
|
16
16
|
const size = SIZE.alloc();
|
|
17
|
-
gui.SendMessage(hwnd, 5633 /*
|
|
17
|
+
gui.SendMessage(hwnd, 5633 /* ButtonExtMsg.GETIDEALSIZE */, 0, size.ptr);
|
|
18
18
|
const { cx, cy } = SIZE.decode(size);
|
|
19
19
|
return { width: cx, height: cy };
|
|
20
20
|
}
|
|
@@ -23,7 +23,7 @@ export function measureTextForHwnd(hwnd, text) {
|
|
|
23
23
|
if (!hdc)
|
|
24
24
|
return { width: 0, height: 0 };
|
|
25
25
|
// SendMessage 返回 LRESULT 裸数;GETFONT 语义为 HFONT——断言进 GDI 对象品牌链
|
|
26
|
-
const hFont = gui.SendMessage(hwnd, 49 /*
|
|
26
|
+
const hFont = gui.SendMessage(hwnd, 49 /* WmMsg.GETFONT */, 0, 0);
|
|
27
27
|
const oldFont = hFont ? SelectObject(hdc, hFont) : NULL;
|
|
28
28
|
const result = measureText(hdc, text, 0);
|
|
29
29
|
if (hFont)
|
package/lib/text-measure.ts
CHANGED
|
@@ -3,12 +3,13 @@ import * as gui from 'gui'
|
|
|
3
3
|
import { DrawText, GetDC, ReleaseDC } from './windows/user32.js'
|
|
4
4
|
import { SelectObject } from './windows/gdi32.js'
|
|
5
5
|
import { RECT, SIZE } from './windows/structs.js'
|
|
6
|
+
import { ButtonExtMsg, DrawTextFlag, WmMsg } from '../constants'
|
|
6
7
|
|
|
7
8
|
export function measureText(hdc: Ptr<'HDC'>, text: string, maxWidth: number): { width: number; height: number } {
|
|
8
9
|
// rect 双向位不注册 encoder(CALCRECT 就地写回):encode(初值) 新建 + DeepPartial 初值一步到位
|
|
9
10
|
//(left/top/bottom 缺省跳过保持 0)→ 传 .ptr → decode 读回
|
|
10
11
|
const r = RECT.encode({ right: maxWidth })
|
|
11
|
-
DrawText(hdc, text, -1, r.ptr,
|
|
12
|
+
DrawText(hdc, text, -1, r.ptr, DrawTextFlag.CALCRECT)
|
|
12
13
|
const { right, bottom } = RECT.decode(r)
|
|
13
14
|
return { width: right, height: bottom }
|
|
14
15
|
}
|
|
@@ -16,7 +17,7 @@ export function measureText(hdc: Ptr<'HDC'>, text: string, maxWidth: number): {
|
|
|
16
17
|
export function getButtonIdealSize(hwnd: gui.HWND): { width: number; height: number } {
|
|
17
18
|
// SIZE 纯出参位(GETIDEALSIZE 就地写回):alloc → 传 .ptr → decode 读回
|
|
18
19
|
const size = SIZE.alloc()
|
|
19
|
-
gui.SendMessage(hwnd,
|
|
20
|
+
gui.SendMessage(hwnd, ButtonExtMsg.GETIDEALSIZE, 0, size.ptr)
|
|
20
21
|
const { cx, cy } = SIZE.decode(size)
|
|
21
22
|
return { width: cx, height: cy }
|
|
22
23
|
}
|
|
@@ -25,7 +26,7 @@ export function measureTextForHwnd(hwnd: gui.HWND, text: string): { width: numbe
|
|
|
25
26
|
const hdc = GetDC(hwnd)
|
|
26
27
|
if (!hdc) return { width: 0, height: 0 }
|
|
27
28
|
// SendMessage 返回 LRESULT 裸数;GETFONT 语义为 HFONT——断言进 GDI 对象品牌链
|
|
28
|
-
const hFont = gui.SendMessage(hwnd,
|
|
29
|
+
const hFont = gui.SendMessage(hwnd, WmMsg.GETFONT, 0, 0) as Ptr<'HGDIOBJ'>
|
|
29
30
|
const oldFont = hFont ? SelectObject(hdc, hFont) : NULL
|
|
30
31
|
const result = measureText(hdc, text, 0)
|
|
31
32
|
if (hFont) SelectObject(hdc, oldFont)
|