quickwin 3.0.0 → 3.0.1
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/lib/abort.d.ts +21 -0
- package/lib/assert.d.ts +2 -0
- package/lib/bitmap.d.ts +7 -0
- package/lib/cache-utils.d.ts +6 -0
- package/lib/certs.d.ts +1 -0
- package/lib/dpi.d.ts +9 -0
- package/lib/dumpRects.d.ts +2 -0
- package/lib/exec-log.d.ts +23 -0
- package/lib/fetch.d.ts +4 -0
- package/lib/ffi/bind.d.ts +70 -0
- package/lib/ffi/ctype.d.ts +117 -0
- package/lib/ffi/index.d.ts +2 -0
- package/lib/ffi/struct.d.ts +174 -0
- package/lib/http-common.d.ts +143 -0
- package/lib/http-server.d.ts +59 -0
- package/lib/polyfill.d.ts +22 -0
- package/lib/react-qw/components/Button.d.ts +9 -0
- package/lib/react-qw/components/CheckBox.d.ts +11 -0
- package/lib/react-qw/components/ComboBox.d.ts +11 -0
- package/lib/react-qw/components/DateTimePicker.d.ts +15 -0
- package/lib/react-qw/components/Input.d.ts +15 -0
- package/lib/react-qw/components/Link.d.ts +12 -0
- package/lib/react-qw/components/ListBox.d.ts +13 -0
- package/lib/react-qw/components/ListView.d.ts +37 -0
- package/lib/react-qw/components/PathPicker.d.ts +23 -0
- package/lib/react-qw/components/ProgressBar.d.ts +9 -0
- package/lib/react-qw/components/RadioButton.d.ts +11 -0
- package/lib/react-qw/components/ScrollView.d.ts +9 -0
- package/lib/react-qw/components/Slider.d.ts +12 -0
- package/lib/react-qw/components/Tab.d.ts +14 -0
- package/lib/react-qw/components/Tooltip.d.ts +7 -0
- package/lib/react-qw/components/TreeView.d.ts +18 -0
- package/lib/react-qw/components/index.d.ts +16 -0
- package/lib/react-qw/index.d.ts +24 -0
- package/lib/react-qw/layout.d.ts +29 -0
- package/lib/react-qw/nmhdr.d.ts +1 -0
- package/lib/react-qw/props.d.ts +2 -0
- package/lib/react-qw/reconciler.d.ts +21 -0
- package/lib/stream.d.ts +20 -0
- package/lib/text-codec.d.ts +39 -0
- package/lib/text-measure.d.ts +14 -0
- package/lib/url.d.ts +54 -0
- package/lib/websocket.d.ts +73 -0
- package/lib/windows/{advapi32.ts → advapi32.d.ts} +6 -21
- package/lib/windows/comctl32.d.ts +14 -0
- package/lib/windows/comdlg32.d.ts +4 -0
- package/lib/windows/{gdi32.ts → gdi32.d.ts} +161 -98
- package/lib/windows/{kernel32.ts → kernel32.d.ts} +138 -115
- package/lib/windows/{msimg32.ts → msimg32.d.ts} +11 -14
- package/lib/windows/ntdll.d.ts +5 -0
- package/lib/windows/ole32.d.ts +3 -0
- package/lib/windows/{psapi.ts → psapi.d.ts} +9 -18
- package/lib/windows/{shell32.ts → shell32.d.ts} +25 -24
- package/lib/windows/structs.d.ts +3141 -0
- package/lib/windows/{user32.ts → user32.d.ts} +196 -193
- package/lib/windows/version.d.ts +27 -0
- package/lib/windows/{winspool.ts → winspool.d.ts} +18 -32
- package/package.json +15 -6
- 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/lib/abort.ts +0 -69
- package/lib/assert.ts +0 -7
- package/lib/bitmap.ts +0 -39
- package/lib/cache-utils.ts +0 -40
- package/lib/certs.ts +0 -258
- package/lib/dpi.ts +0 -31
- package/lib/dumpRects.ts +0 -34
- package/lib/exec-log.ts +0 -30
- package/lib/fetch.ts +0 -589
- package/lib/ffi/bind.ts +0 -540
- package/lib/ffi/ctype.ts +0 -371
- package/lib/ffi/index.ts +0 -6
- package/lib/ffi/struct.ts +0 -780
- package/lib/http-common.ts +0 -441
- package/lib/http-server.ts +0 -604
- package/lib/polyfill.ts +0 -171
- package/lib/react-qw/components/index.ts +0 -17
- package/lib/react-qw/index.ts +0 -96
- package/lib/react-qw/layout.ts +0 -111
- package/lib/react-qw/nmhdr.ts +0 -7
- package/lib/react-qw/props.ts +0 -41
- package/lib/react-qw/reconciler.ts +0 -379
- package/lib/stream.ts +0 -22
- package/lib/text-codec.ts +0 -171
- package/lib/text-measure.ts +0 -44
- package/lib/url.ts +0 -201
- package/lib/websocket.ts +0 -600
- package/lib/windows/comctl32.ts +0 -20
- package/lib/windows/comdlg32.ts +0 -10
- package/lib/windows/ntdll.ts +0 -11
- package/lib/windows/ole32.ts +0 -9
- package/lib/windows/structs.ts +0 -807
- package/lib/windows/version.ts +0 -35
package/lib/polyfill.ts
DELETED
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
import * as os from 'os'
|
|
2
|
-
import './url.js'
|
|
3
|
-
import './text-codec.js'
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
declare global {
|
|
7
|
-
interface Console {
|
|
8
|
-
error: (...args: unknown[]) => void
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
var window: typeof globalThis
|
|
12
|
-
|
|
13
|
-
type TimerId = os.TimerId
|
|
14
|
-
function setTimeout<P extends unknown[]>(fn: (...argsf: P) => void, ms?: number, ...argsr: P): TimerId
|
|
15
|
-
function clearTimeout(id: TimerId): void
|
|
16
|
-
|
|
17
|
-
function btoa(data: string): string
|
|
18
|
-
function atob(data: string): string
|
|
19
|
-
|
|
20
|
-
interface SubtleCrypto {
|
|
21
|
-
digest(algorithm: 'SHA-1', data: ArrayBuffer | Uint8Array): Promise<ArrayBuffer>
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
interface Crypto {
|
|
25
|
-
getRandomValues<T extends ArrayBufferView>(array: T): T
|
|
26
|
-
subtle: SubtleCrypto
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
var crypto: Crypto
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// 1. window — triggers Emscripten browser path
|
|
33
|
-
if (typeof globalThis.window === 'undefined')
|
|
34
|
-
globalThis.window = globalThis
|
|
35
|
-
|
|
36
|
-
// 2. setTimeout polyfill — bridge to os.setTimeout
|
|
37
|
-
if (typeof globalThis.setTimeout === 'undefined') {
|
|
38
|
-
globalThis.setTimeout = ((fn, ms, ...args) => {
|
|
39
|
-
return os.setTimeout(() => fn(...args), ms ?? 0)
|
|
40
|
-
}) satisfies typeof setTimeout
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// 2b. clearTimeout polyfill — bridge to os.clearTimeout
|
|
44
|
-
if (typeof globalThis.clearTimeout === 'undefined') {
|
|
45
|
-
globalThis.clearTimeout = os.clearTimeout
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// 3. console.error polyfill — QuickJS only has console.log
|
|
49
|
-
if (typeof console.error === 'undefined') {
|
|
50
|
-
console.error = (...args: unknown[]) => { console.log(...args) }
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
// ── SHA-1 ──
|
|
55
|
-
|
|
56
|
-
function sha1Bytes(bytes: number[]): ArrayBuffer {
|
|
57
|
-
const len = bytes.length
|
|
58
|
-
const ml = len * 8
|
|
59
|
-
|
|
60
|
-
bytes.push(0x80)
|
|
61
|
-
while (bytes.length % 64 !== 56) { bytes.push(0) }
|
|
62
|
-
bytes.push(0)
|
|
63
|
-
bytes.push(0)
|
|
64
|
-
bytes.push(0)
|
|
65
|
-
bytes.push(0)
|
|
66
|
-
bytes.push((ml >>> 24) & 0xFF)
|
|
67
|
-
bytes.push((ml >>> 16) & 0xFF)
|
|
68
|
-
bytes.push((ml >>> 8) & 0xFF)
|
|
69
|
-
bytes.push(ml & 0xFF)
|
|
70
|
-
|
|
71
|
-
let h0 = 0x67452301, h1 = 0xEFCDAB89
|
|
72
|
-
let h2 = 0x98BADCFE, h3 = 0x10325476, h4 = 0xC3D2E1F0
|
|
73
|
-
const rotl = (x: number, n: number): number => ((x << n) | (x >>> (32 - n))) >>> 0
|
|
74
|
-
|
|
75
|
-
for (let i = 0; i < bytes.length; i += 64) {
|
|
76
|
-
const w: number[] = []
|
|
77
|
-
for (let t = 0; t < 16; t++) {
|
|
78
|
-
w[t] = (bytes[i + 4 * t]! << 24) | (bytes[i + 4 * t + 1]! << 16) |
|
|
79
|
-
(bytes[i + 4 * t + 2]! << 8) | bytes[i + 4 * t + 3]!
|
|
80
|
-
}
|
|
81
|
-
for (let t = 16; t < 80; t++) {
|
|
82
|
-
w[t] = rotl(w[t - 3]! ^ w[t - 8]! ^ w[t - 14]! ^ w[t - 16]!, 1)
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
let a = h0, b = h1, c = h2, d = h3, e = h4
|
|
86
|
-
for (let t = 0; t < 80; t++) {
|
|
87
|
-
let f: number, k: number
|
|
88
|
-
if (t < 20) { f = (b & c) | (~b & d); k = 0x5A827999 }
|
|
89
|
-
else if (t < 40) { f = b ^ c ^ d; k = 0x6ED9EBA1 }
|
|
90
|
-
else if (t < 60) { f = (b & c) | (b & d) | (c & d); k = 0x8F1BBCDC }
|
|
91
|
-
else { f = b ^ c ^ d; k = 0xCA62C1D6 }
|
|
92
|
-
const temp = (rotl(a, 5) + f + e + k + w[t]!) >>> 0
|
|
93
|
-
e = d; d = c; c = rotl(b, 30); b = a; a = temp
|
|
94
|
-
}
|
|
95
|
-
h0 = (h0 + a) >>> 0; h1 = (h1 + b) >>> 0
|
|
96
|
-
h2 = (h2 + c) >>> 0; h3 = (h3 + d) >>> 0; h4 = (h4 + e) >>> 0
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const result = new Uint8Array(20)
|
|
100
|
-
const w32 = (off: number, v: number) => {
|
|
101
|
-
result[off] = (v >>> 24) & 0xFF; result[off + 1] = (v >>> 16) & 0xFF
|
|
102
|
-
result[off + 2] = (v >>> 8) & 0xFF; result[off + 3] = v & 0xFF
|
|
103
|
-
}
|
|
104
|
-
w32(0, h0); w32(4, h1); w32(8, h2); w32(12, h3); w32(16, h4)
|
|
105
|
-
return result.buffer
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// ── Base64 ──
|
|
109
|
-
|
|
110
|
-
const B64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
|
|
111
|
-
|
|
112
|
-
function b64Encode(data: Uint8Array): string {
|
|
113
|
-
let r = ''
|
|
114
|
-
for (let i = 0; i < data.length; i += 3) {
|
|
115
|
-
const b0 = data[i]!, b1 = i + 1 < data.length ? data[i + 1]! : 0, b2 = i + 2 < data.length ? data[i + 2]! : 0
|
|
116
|
-
r += B64[b0 >> 2]! + B64[((b0 & 3) << 4) | (b1 >> 4)]!
|
|
117
|
-
r += i + 1 < data.length ? B64[((b1 & 15) << 2) | (b2 >> 6)]! : '='
|
|
118
|
-
r += i + 2 < data.length ? B64[b2 & 63]! : '='
|
|
119
|
-
}
|
|
120
|
-
return r
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function b64Decode(str: string): Uint8Array {
|
|
124
|
-
str = str.replace(/[^A-Za-z0-9+/=]/g, '')
|
|
125
|
-
const bytes: number[] = []
|
|
126
|
-
for (let i = 0; i < str.length; i += 4) {
|
|
127
|
-
const c0 = B64.indexOf(str[i]!), c1 = B64.indexOf(str[i + 1]!)
|
|
128
|
-
const c2 = B64.indexOf(str[i + 2]!), c3 = B64.indexOf(str[i + 3]!)
|
|
129
|
-
bytes.push((c0 << 2) | (c1 >> 4))
|
|
130
|
-
if (c2 >= 0) bytes.push(((c1 & 15) << 4) | (c2 >> 2))
|
|
131
|
-
if (c3 >= 0) bytes.push(((c2 & 3) << 6) | c3)
|
|
132
|
-
}
|
|
133
|
-
return new Uint8Array(bytes)
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// ── btoa / atob ──
|
|
137
|
-
|
|
138
|
-
if (typeof globalThis.btoa === 'undefined') {
|
|
139
|
-
globalThis.btoa = (data: string): string => {
|
|
140
|
-
const bytes = new Uint8Array(data.length)
|
|
141
|
-
for (let i = 0; i < data.length; i++) bytes[i] = data.charCodeAt(i) & 0xFF
|
|
142
|
-
return b64Encode(bytes)
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
if (typeof globalThis.atob === 'undefined') {
|
|
147
|
-
globalThis.atob = (data: string): string => {
|
|
148
|
-
const bytes = b64Decode(data)
|
|
149
|
-
let r = ''
|
|
150
|
-
for (let i = 0; i < bytes.length; i++) r += String.fromCharCode(bytes[i]!)
|
|
151
|
-
return r
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
if (typeof globalThis.crypto === 'undefined') {
|
|
156
|
-
globalThis.crypto = {
|
|
157
|
-
getRandomValues: <T extends ArrayBufferView>(array: T): T => {
|
|
158
|
-
const view = new Uint8Array(array.buffer, array.byteOffset, array.byteLength)
|
|
159
|
-
for (let i = 0; i < view.length; i++) view[i] = (Math.random() * 256) | 0
|
|
160
|
-
return array
|
|
161
|
-
},
|
|
162
|
-
subtle: {
|
|
163
|
-
digest: (algorithm: string, data: ArrayBuffer | Uint8Array): Promise<ArrayBuffer> => {
|
|
164
|
-
if (algorithm.toUpperCase() !== 'SHA-1')
|
|
165
|
-
return Promise.reject(new Error('Digest: ' + algorithm + ' not supported'))
|
|
166
|
-
const bytes = data instanceof Uint8Array ? Array.from(data) : Array.from(new Uint8Array(data))
|
|
167
|
-
return Promise.resolve(sha1Bytes(bytes))
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
export { Button, type ButtonProps } from './Button.js'
|
|
2
|
-
export { CheckBox, type CheckBoxProps } from './CheckBox.js'
|
|
3
|
-
export { ComboBox, type ComboBoxProps } from './ComboBox.js'
|
|
4
|
-
export { DateTimePicker, type DateTimePickerProps } from './DateTimePicker.js'
|
|
5
|
-
export { Input, type InputProps } from './Input.js'
|
|
6
|
-
export { Link, type LinkProps } from './Link.js'
|
|
7
|
-
export { ListBox, type ListBoxProps } from './ListBox.js'
|
|
8
|
-
export { ListView, makeColorBlock, type ListViewProps, type Column, type CellStyle, type Align } from './ListView.js'
|
|
9
|
-
export { ProgressBar, type ProgressBarProps } from './ProgressBar.js'
|
|
10
|
-
export { RadioButton, type RadioButtonProps } from './RadioButton.js'
|
|
11
|
-
export { ScrollView, type ScrollViewProps } from './ScrollView.js'
|
|
12
|
-
export { Slider, type SliderProps } from './Slider.js'
|
|
13
|
-
export { Tab, type TabProps } from './Tab.js'
|
|
14
|
-
export { Tooltip, type TooltipProps } from './Tooltip.js'
|
|
15
|
-
export { TreeView, type TreeViewProps, type TreeNode } from './TreeView.js'
|
|
16
|
-
export { PathPicker, type PathPickerProps } from './PathPicker.js'
|
|
17
|
-
|
package/lib/react-qw/index.ts
DELETED
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import * as gui from 'gui'
|
|
2
|
-
import React from 'react'
|
|
3
|
-
import { DefWindowProc, GetSystemMetrics, ShowWindow, UpdateWindow } from '../windows/user32.js'
|
|
4
|
-
import reconciler, { scaleFactor, instancesByHwnd, forceFlexLayout, type Instance } from './reconciler.js'
|
|
5
|
-
import { CreatePos, ShowWindowCmd, SysMetrics, WindowStyle, WmMsg } from '../../constants'
|
|
6
|
-
|
|
7
|
-
const noop = () => { }
|
|
8
|
-
|
|
9
|
-
export interface RootWindowConfig {
|
|
10
|
-
text?: string
|
|
11
|
-
ws?: number
|
|
12
|
-
x?: number
|
|
13
|
-
y?: number
|
|
14
|
-
width?: number
|
|
15
|
-
height?: number
|
|
16
|
-
noShowWindow?: boolean
|
|
17
|
-
onEvent?: (e: { hwnd: gui.HWND; msg: number; wParam: number; lParam: number }) => number | void
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
let defaultClassRegistered = false
|
|
21
|
-
const DEFAULT_CLASS = '_QuickWinDefault'
|
|
22
|
-
const rootMap = new Map<gui.HWND, { root: ReturnType<typeof reconciler.createContainer>, onEvent?: RootWindowConfig['onEvent'] }>()
|
|
23
|
-
|
|
24
|
-
function ensureDefaultClass() {
|
|
25
|
-
if (defaultClassRegistered) return DEFAULT_CLASS
|
|
26
|
-
gui.RegisterClass(DEFAULT_CLASS, (hwnd, msg, wParam, lParam) => {
|
|
27
|
-
const entry = rootMap.get(hwnd)
|
|
28
|
-
if (msg === WmMsg.DESTROY && entry) {
|
|
29
|
-
rootMap.delete(hwnd)
|
|
30
|
-
reconciler.updateContainer(null, entry.root, null, () => {
|
|
31
|
-
instancesByHwnd.delete(hwnd)
|
|
32
|
-
})
|
|
33
|
-
}
|
|
34
|
-
const result = entry?.onEvent?.({ hwnd, msg, wParam, lParam })
|
|
35
|
-
if (typeof result === 'number') return result
|
|
36
|
-
if (msg === WmMsg.SIZE) forceFlexLayout(hwnd)
|
|
37
|
-
return DefWindowProc(hwnd, msg, wParam, lParam)
|
|
38
|
-
})
|
|
39
|
-
defaultClassRegistered = true
|
|
40
|
-
return DEFAULT_CLASS
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export function createRoot(container: gui.HWND | RootWindowConfig) {
|
|
44
|
-
let hwnd: gui.HWND
|
|
45
|
-
if (typeof container === 'object') {
|
|
46
|
-
const cfg = container
|
|
47
|
-
const className = ensureDefaultClass()
|
|
48
|
-
const ws = (cfg.ws ?? 0) | WindowStyle.OVERLAPPEDWINDOW
|
|
49
|
-
const w = (cfg.width ?? 800) * scaleFactor
|
|
50
|
-
const h = (cfg.height ?? 600) * scaleFactor
|
|
51
|
-
const [sw, sh] = [GetSystemMetrics(SysMetrics.CXSCREEN), GetSystemMetrics(SysMetrics.CYSCREEN)]
|
|
52
|
-
const x = cfg.x === undefined ? ((sw - w) / 2) | 0
|
|
53
|
-
: cfg.x === CreatePos.USEDEFAULT ? CreatePos.USEDEFAULT
|
|
54
|
-
: cfg.x * scaleFactor
|
|
55
|
-
const y = cfg.y === undefined ? ((sh - h) / 2) | 0
|
|
56
|
-
: cfg.y === CreatePos.USEDEFAULT ? CreatePos.USEDEFAULT
|
|
57
|
-
: cfg.y * scaleFactor
|
|
58
|
-
const hwin = gui.CreateWindow(className, cfg.text || '', ws, x, y, w, h, null, null)
|
|
59
|
-
if (!hwin) throw new Error('CreateWindow failed')
|
|
60
|
-
hwnd = hwin
|
|
61
|
-
// 启动首帧:ShowWindow 后立即 WM_PAINT,不留白(原 gui.ShowWindow 复合语义)
|
|
62
|
-
if (!cfg.noShowWindow) { ShowWindow(hwnd, ShowWindowCmd.SHOWNORMAL); UpdateWindow(hwnd) }
|
|
63
|
-
} else {
|
|
64
|
-
hwnd = container
|
|
65
|
-
}
|
|
66
|
-
const root = reconciler.createContainer(
|
|
67
|
-
hwnd, 0, null, false, null, '',
|
|
68
|
-
(error, info) => console.log('[React uncaught]', error, error.stack, info.componentStack),
|
|
69
|
-
(error, info) => console.log('[React caught]', error, error.stack, info.componentStack),
|
|
70
|
-
(error, info) => console.log('[React recoverable]', error, error.stack, info.componentStack),
|
|
71
|
-
noop,
|
|
72
|
-
)
|
|
73
|
-
const rootInst: Instance = { hwnd, type: '_root', props: { type: '_root', style: { flexDirection: 'column', alignItems: 'stretch' } }, children: [] }
|
|
74
|
-
instancesByHwnd.set(hwnd, rootInst)
|
|
75
|
-
if (typeof container === 'object')
|
|
76
|
-
rootMap.set(hwnd, { root, onEvent: container.onEvent })
|
|
77
|
-
return {
|
|
78
|
-
hwnd,
|
|
79
|
-
render(element: React.ReactNode) {
|
|
80
|
-
reconciler.updateContainer(element, root, null, noop)
|
|
81
|
-
},
|
|
82
|
-
unmount() {
|
|
83
|
-
reconciler.updateContainer(null, root, null, noop)
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export * from './components/index.js'
|
|
89
|
-
|
|
90
|
-
export function render(element: React.ReactNode, containerOrConfig?: gui.HWND | RootWindowConfig | null, _callback?: () => void) {
|
|
91
|
-
if (containerOrConfig == null) {
|
|
92
|
-
throw new Error('render() requires a window handle or RootWindowConfig as second argument')
|
|
93
|
-
}
|
|
94
|
-
const root = createRoot(containerOrConfig)
|
|
95
|
-
root.render(element)
|
|
96
|
-
}
|
package/lib/react-qw/layout.ts
DELETED
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
export type JustifyContent = 'flex-start' | 'flex-end' | 'center' | 'space-between' | 'space-around' | 'space-evenly'
|
|
2
|
-
|
|
3
|
-
export interface FlexStyle {
|
|
4
|
-
flexDirection?: 'row' | 'column'
|
|
5
|
-
justifyContent?: JustifyContent
|
|
6
|
-
alignItems?: 'flex-start' | 'flex-end' | 'center' | 'stretch'
|
|
7
|
-
gap?: number
|
|
8
|
-
padding?: number
|
|
9
|
-
paddingTop?: number
|
|
10
|
-
paddingRight?: number
|
|
11
|
-
paddingBottom?: number
|
|
12
|
-
paddingLeft?: number
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
interface ChildStyle {
|
|
16
|
-
width?: number | 'auto'; height?: number | 'auto'
|
|
17
|
-
flexGrow?: number
|
|
18
|
-
alignSelf?: 'auto' | 'flex-start' | 'flex-end' | 'center' | 'stretch'
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
interface ChildInfo {
|
|
22
|
-
style: ChildStyle
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export interface LayoutResult {
|
|
26
|
-
x: number; y: number; width: number; height: number
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const DEFAULT_W = 100
|
|
30
|
-
const DEFAULT_H = 30
|
|
31
|
-
|
|
32
|
-
export function calculateFlexLayout(
|
|
33
|
-
flex: FlexStyle,
|
|
34
|
-
parentW: number,
|
|
35
|
-
parentH: number,
|
|
36
|
-
children: ChildInfo[]
|
|
37
|
-
): LayoutResult[] {
|
|
38
|
-
const dir = flex.flexDirection || 'column'
|
|
39
|
-
const gap = flex.gap ?? 0
|
|
40
|
-
const justify = flex.justifyContent || 'flex-start'
|
|
41
|
-
const align = flex.alignItems || 'stretch'
|
|
42
|
-
const isRow = dir === 'row'
|
|
43
|
-
const n = children.length
|
|
44
|
-
|
|
45
|
-
const mainSize: 'w' | 'h' = isRow ? 'w' : 'h'
|
|
46
|
-
const crossSize: 'w' | 'h' = isRow ? 'h' : 'w'
|
|
47
|
-
const mainPos: 'x' | 'y' = isRow ? 'x' : 'y'
|
|
48
|
-
const crossPos: 'x' | 'y' = isRow ? 'y' : 'x'
|
|
49
|
-
const mainDim: 'width' | 'height' = isRow ? 'width' : 'height'
|
|
50
|
-
const crossDim: 'width' | 'height' = isRow ? 'height' : 'width'
|
|
51
|
-
const pt = flex.paddingTop ?? flex.padding ?? 0
|
|
52
|
-
const pr = flex.paddingRight ?? flex.padding ?? 0
|
|
53
|
-
const pb = flex.paddingBottom ?? flex.padding ?? 0
|
|
54
|
-
const pl = flex.paddingLeft ?? flex.padding ?? 0
|
|
55
|
-
const mainPadding = isRow ? pl + pr : pt + pb
|
|
56
|
-
const crossPadding = isRow ? pt + pb : pl + pr
|
|
57
|
-
const parentMain = (isRow ? parentW : parentH) - mainPadding
|
|
58
|
-
const parentCross = (isRow ? parentH : parentW) - crossPadding
|
|
59
|
-
|
|
60
|
-
const sizes = children.map(c => ({
|
|
61
|
-
w: typeof c.style.width === 'number' ? c.style.width : DEFAULT_W,
|
|
62
|
-
h: typeof c.style.height === 'number' ? c.style.height : DEFAULT_H,
|
|
63
|
-
}))
|
|
64
|
-
|
|
65
|
-
const flexGrows = children.map(c => c.style.flexGrow ?? 0)
|
|
66
|
-
const totalGrow = flexGrows.reduce((s, g) => s + g, 0)
|
|
67
|
-
|
|
68
|
-
const baseMain = sizes.reduce((s, c) => s + c[mainSize], 0)
|
|
69
|
-
let freeGrow = parentMain - baseMain - Math.max(0, n - 1) * gap
|
|
70
|
-
if (totalGrow > 0 && freeGrow > 0) {
|
|
71
|
-
for (let i = 0; i < n; i++) {
|
|
72
|
-
sizes[i]![mainSize] += freeGrow * flexGrows[i]! / totalGrow
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const totalMain = sizes.reduce((s, c) => s + c[mainSize], 0)
|
|
77
|
-
const free = parentMain - totalMain - Math.max(0, n - 1) * gap
|
|
78
|
-
let offset = 0, extraGap = 0
|
|
79
|
-
if (justify === 'flex-end') offset = free
|
|
80
|
-
else if (justify === 'center') offset = Math.max(0, free / 2)
|
|
81
|
-
else if (justify === 'space-between' && n > 1) extraGap = free / (n - 1)
|
|
82
|
-
else if (justify === 'space-around') { extraGap = free / n; offset = extraGap / 2 }
|
|
83
|
-
else if (justify === 'space-evenly') { extraGap = free / (n + 1); offset = extraGap }
|
|
84
|
-
|
|
85
|
-
let cursor = offset
|
|
86
|
-
return sizes.map((sz, i) => {
|
|
87
|
-
const childCrossBase = sz[crossSize]
|
|
88
|
-
const childAlign = children[i]!.style.alignSelf ?? 'auto'
|
|
89
|
-
const effectiveAlign = childAlign === 'auto' ? align : childAlign
|
|
90
|
-
let childMain = sz[mainSize]
|
|
91
|
-
let childCross = childCrossBase
|
|
92
|
-
const res: LayoutResult = { x: 0, y: 0, width: 0, height: 0 }
|
|
93
|
-
res[mainPos] = cursor
|
|
94
|
-
res[crossPos] = 0
|
|
95
|
-
res[mainDim] = childMain
|
|
96
|
-
res[crossDim] = childCross
|
|
97
|
-
if (effectiveAlign === 'stretch') {
|
|
98
|
-
const hasExplicitCross = typeof children[i]!.style[crossDim] === 'number'
|
|
99
|
-
if (!hasExplicitCross) {
|
|
100
|
-
res[crossDim] = parentCross
|
|
101
|
-
res[crossPos] = 0
|
|
102
|
-
}
|
|
103
|
-
} else if (effectiveAlign === 'flex-end') {
|
|
104
|
-
res[crossPos] = parentCross - childCross
|
|
105
|
-
} else if (effectiveAlign === 'center') {
|
|
106
|
-
res[crossPos] = Math.max(0, (parentCross - childCross) / 2)
|
|
107
|
-
}
|
|
108
|
-
cursor += childMain + gap + extraGap
|
|
109
|
-
return res
|
|
110
|
-
})
|
|
111
|
-
}
|
package/lib/react-qw/nmhdr.ts
DELETED
package/lib/react-qw/props.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import * as gui from 'gui'
|
|
2
|
-
import { SetWindowText, EnableWindow, ShowWindow } from '../windows/user32.js'
|
|
3
|
-
import type { Instance, Props } from './reconciler.js'
|
|
4
|
-
import { EditMsg, ShowWindowCmd } from '../../constants'
|
|
5
|
-
|
|
6
|
-
export function applyProps(
|
|
7
|
-
instance: Instance,
|
|
8
|
-
newProps: Props,
|
|
9
|
-
_oldProps: Props,
|
|
10
|
-
) {
|
|
11
|
-
const hwnd = instance.hwnd
|
|
12
|
-
instance.props = newProps
|
|
13
|
-
// 延迟控件(DELAYED_CONTROLS)建窗前 hwnd 为 null:窗口操作此刻全是 no-op
|
|
14
|
-
// (text/ws/style 在 ensureChildWindow 建窗时应用),FFI 位不收 JS null——早退。
|
|
15
|
-
if (hwnd === null) return
|
|
16
|
-
|
|
17
|
-
const textVal = 'text' in newProps ? newProps.text : (
|
|
18
|
-
typeof newProps.children === 'string' ? newProps.children :
|
|
19
|
-
typeof newProps.children === 'number' ? String(newProps.children) : undefined
|
|
20
|
-
)
|
|
21
|
-
if (textVal !== undefined) {
|
|
22
|
-
let cursor = -1
|
|
23
|
-
if (instance.type === 'EDIT') {
|
|
24
|
-
const sel = gui.SendMessage(hwnd, EditMsg.GETSEL, 0, 0)
|
|
25
|
-
cursor = sel >>> 16
|
|
26
|
-
}
|
|
27
|
-
SetWindowText(hwnd, textVal)
|
|
28
|
-
if (instance.type === 'EDIT' && cursor >= 0) {
|
|
29
|
-
gui.SendMessage(hwnd, EditMsg.SETSEL, cursor, cursor)
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
if ('disabled' in newProps) {
|
|
33
|
-
EnableWindow(hwnd, !newProps.disabled ? 1 : 0)
|
|
34
|
-
}
|
|
35
|
-
if ('hidden' in newProps) {
|
|
36
|
-
ShowWindow(hwnd, newProps.hidden ? ShowWindowCmd.HIDE : ShowWindowCmd.SHOW)
|
|
37
|
-
}
|
|
38
|
-
if ('visible' in newProps) {
|
|
39
|
-
ShowWindow(hwnd, newProps.visible ? ShowWindowCmd.SHOW : ShowWindowCmd.HIDE)
|
|
40
|
-
}
|
|
41
|
-
}
|