winui-svelte 0.0.2 → 0.0.4
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/API.md +120 -6
- package/README.md +70 -90
- package/dist/bindings/.dynwinrt-managed +1 -1
- package/dist/components/convert.d.ts +3 -3
- package/dist/components/convert.d.ts.map +1 -1
- package/dist/components/fluent-glyph.d.ts +4 -0
- package/dist/components/fluent-glyph.d.ts.map +1 -1
- package/dist/components/generated/controls/FontIcon.svelte.d.ts +1 -1
- package/dist/components/generated/controls/FontIconSource.svelte.d.ts +1 -1
- package/dist/components/generated/controls/UIElement.svelte.d.ts +1 -1
- package/dist/components/index.d.ts +4 -2
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js.map +1 -1
- package/dist/css/runtime/resources.d.ts +1 -1
- package/dist/dev/dev-window.d.ts +23 -0
- package/dist/dev/dev-window.d.ts.map +1 -0
- package/dist/dev/dev-window.js +42 -0
- package/dist/dev/dev-window.js.map +1 -0
- package/dist/dev/index.d.ts +3 -0
- package/dist/dev/index.d.ts.map +1 -0
- package/dist/dev/index.js +2 -0
- package/dist/dev/index.js.map +1 -0
- package/dist/dev/start-dev.d.ts +47 -0
- package/dist/dev/start-dev.d.ts.map +1 -0
- package/dist/dev/start-dev.js +81 -0
- package/dist/dev/start-dev.js.map +1 -0
- package/dist/enums.d.ts +2 -0
- package/dist/enums.d.ts.map +1 -1
- package/dist/enums.js +2 -0
- package/dist/enums.js.map +1 -1
- package/dist/media/media-player.d.ts +8 -0
- package/dist/media/media-player.d.ts.map +1 -1
- package/dist/media/media-player.js +15 -0
- package/dist/media/media-player.js.map +1 -1
- package/dist/web-apis/window-geometry.d.ts +39 -0
- package/dist/web-apis/window-geometry.d.ts.map +1 -0
- package/dist/web-apis/window-geometry.js +95 -0
- package/dist/web-apis/window-geometry.js.map +1 -0
- package/dist/win2d/Canvas.svelte +1 -0
- package/dist/window/anchor.d.ts +92 -0
- package/dist/window/anchor.d.ts.map +1 -0
- package/dist/window/anchor.js +100 -0
- package/dist/window/anchor.js.map +1 -0
- package/dist/window/backdrop.d.ts +4 -1
- package/dist/window/backdrop.d.ts.map +1 -1
- package/dist/window/backdrop.js +10 -2
- package/dist/window/backdrop.js.map +1 -1
- package/dist/window/create-window.d.ts +1 -1
- package/dist/window/create-window.d.ts.map +1 -1
- package/dist/window/create-window.js +3 -1
- package/dist/window/create-window.js.map +1 -1
- package/dist/window/css-pixels.d.ts +14 -0
- package/dist/window/css-pixels.d.ts.map +1 -0
- package/dist/window/css-pixels.js +12 -0
- package/dist/window/css-pixels.js.map +1 -0
- package/dist/window/dock.d.ts +51 -0
- package/dist/window/dock.d.ts.map +1 -0
- package/dist/window/dock.js +69 -0
- package/dist/window/dock.js.map +1 -0
- package/dist/window/index.d.ts +4 -0
- package/dist/window/index.d.ts.map +1 -1
- package/dist/window/index.js +2 -0
- package/dist/window/index.js.map +1 -1
- package/dist/window/native.d.ts +2 -0
- package/dist/window/native.d.ts.map +1 -1
- package/dist/window/native.js +10 -0
- package/dist/window/native.js.map +1 -1
- package/dist/window/transparent-backdrop.d.ts +11 -0
- package/dist/window/transparent-backdrop.d.ts.map +1 -0
- package/dist/window/transparent-backdrop.js +35 -0
- package/dist/window/transparent-backdrop.js.map +1 -0
- package/dist/window/window-handle.d.ts +17 -0
- package/dist/window/window-handle.d.ts.map +1 -1
- package/dist/window/window-handle.js +23 -0
- package/dist/window/window-handle.js.map +1 -1
- package/dist/window/window-host.svelte.d.ts +17 -12
- package/dist/window/window-host.svelte.d.ts.map +1 -1
- package/dist/window/window-host.svelte.js +208 -23
- package/dist/window/window-host.svelte.js.map +1 -1
- package/dist/winui.d.ts +5 -0
- package/dist/winui.d.ts.map +1 -1
- package/dist/winui.js +5 -0
- package/dist/winui.js.map +1 -1
- package/package.json +25 -7
- package/src/components/convert.ts +3 -3
- package/src/components/fluent-glyph.ts +6 -0
- package/src/components/generated/controls/FontIcon.svelte.ts +1 -1
- package/src/components/generated/controls/FontIconSource.svelte.ts +1 -1
- package/src/components/generated/controls/UIElement.svelte.ts +1 -1
- package/src/components/index.ts +13 -2
- package/src/dev/dev-window.ts +63 -0
- package/src/dev/index.ts +2 -0
- package/src/dev/start-dev.ts +120 -0
- package/src/enums.ts +2 -0
- package/src/media/media-player.ts +17 -0
- package/src/web-apis/window-geometry.ts +106 -0
- package/src/window/anchor.ts +156 -0
- package/src/window/backdrop.ts +11 -2
- package/src/window/create-window.ts +5 -1
- package/src/window/css-pixels.ts +18 -0
- package/src/window/dock.ts +99 -0
- package/src/window/index.ts +4 -0
- package/src/window/native.ts +12 -0
- package/src/window/transparent-backdrop.ts +38 -0
- package/src/window/window-handle.ts +26 -0
- package/src/window/window-host.svelte.ts +256 -36
- package/src/winui.ts +5 -0
- package/web-types.json +3203 -1507
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A corner of a window or of a display's work area. A window that changes
|
|
3
|
+
* size keeps its anchor corner where it is; a window docked to an anchor sits
|
|
4
|
+
* against that corner.
|
|
5
|
+
*/
|
|
6
|
+
export const WindowAnchor = {
|
|
7
|
+
topLeft: "topLeft",
|
|
8
|
+
topRight: "topRight",
|
|
9
|
+
bottomLeft: "bottomLeft",
|
|
10
|
+
bottomRight: "bottomRight"
|
|
11
|
+
} as const;
|
|
12
|
+
|
|
13
|
+
/** One of `WindowAnchor`'s values. */
|
|
14
|
+
export type WindowAnchor = (typeof WindowAnchor)[keyof typeof WindowAnchor];
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Which of a window's sides follow its content: the width, the height, both,
|
|
18
|
+
* or neither (the window keeps the size its props and its user give it).
|
|
19
|
+
*/
|
|
20
|
+
export const SizeToContent = {
|
|
21
|
+
none: "none",
|
|
22
|
+
width: "width",
|
|
23
|
+
height: "height",
|
|
24
|
+
both: "both"
|
|
25
|
+
} as const;
|
|
26
|
+
|
|
27
|
+
/** One of `SizeToContent`'s values. */
|
|
28
|
+
export type SizeToContent = (typeof SizeToContent)[keyof typeof SizeToContent];
|
|
29
|
+
|
|
30
|
+
/** A position and a size in device pixels, as AppWindow and DisplayArea have them. */
|
|
31
|
+
export interface WindowBounds {
|
|
32
|
+
x: number;
|
|
33
|
+
y: number;
|
|
34
|
+
width: number;
|
|
35
|
+
height: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** A size in device pixels. */
|
|
39
|
+
export interface WindowSize {
|
|
40
|
+
width: number;
|
|
41
|
+
height: number;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** The least and the most a window may be, in device pixels; `Infinity` is no most. */
|
|
45
|
+
export interface WindowLimits {
|
|
46
|
+
minWidth: number;
|
|
47
|
+
minHeight: number;
|
|
48
|
+
maxWidth: number;
|
|
49
|
+
maxHeight: number;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const ANCHOR_SIDES = {
|
|
53
|
+
[WindowAnchor.topLeft]: {
|
|
54
|
+
isRight: false,
|
|
55
|
+
isBottom: false
|
|
56
|
+
},
|
|
57
|
+
[WindowAnchor.topRight]: {
|
|
58
|
+
isRight: true,
|
|
59
|
+
isBottom: false
|
|
60
|
+
},
|
|
61
|
+
[WindowAnchor.bottomLeft]: {
|
|
62
|
+
isRight: false,
|
|
63
|
+
isBottom: true
|
|
64
|
+
},
|
|
65
|
+
[WindowAnchor.bottomRight]: {
|
|
66
|
+
isRight: true,
|
|
67
|
+
isBottom: true
|
|
68
|
+
}
|
|
69
|
+
} as const satisfies Record<WindowAnchor, {
|
|
70
|
+
isRight: boolean;
|
|
71
|
+
isBottom: boolean;
|
|
72
|
+
}>;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* The window `bounds` at `size` with its `anchor` corner where it was: a
|
|
76
|
+
* window anchored bottom-right that grows taller grows upward.
|
|
77
|
+
*/
|
|
78
|
+
export function anchoredBounds({ bounds, size, anchor }: {
|
|
79
|
+
bounds: WindowBounds;
|
|
80
|
+
size: WindowSize;
|
|
81
|
+
anchor: WindowAnchor;
|
|
82
|
+
}) {
|
|
83
|
+
const { isRight, isBottom } = ANCHOR_SIDES[anchor];
|
|
84
|
+
return {
|
|
85
|
+
x: isRight ? bounds.x + bounds.width - size.width : bounds.x,
|
|
86
|
+
y: isBottom ? bounds.y + bounds.height - size.height : bounds.y,
|
|
87
|
+
width: size.width,
|
|
88
|
+
height: size.height
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Where a window of `size` sits against the `anchor` corner of `area` (a
|
|
94
|
+
* display's work area), `inset` device pixels in from both of its edges.
|
|
95
|
+
*/
|
|
96
|
+
export function dockedBounds({ area, size, anchor, inset }: {
|
|
97
|
+
area: WindowBounds;
|
|
98
|
+
size: WindowSize;
|
|
99
|
+
anchor: WindowAnchor;
|
|
100
|
+
inset: number;
|
|
101
|
+
}) {
|
|
102
|
+
const { isRight, isBottom } = ANCHOR_SIDES[anchor];
|
|
103
|
+
return {
|
|
104
|
+
x: isRight ? area.x + area.width - size.width - inset : area.x + inset,
|
|
105
|
+
y: isBottom ? area.y + area.height - size.height - inset : area.y + inset,
|
|
106
|
+
width: size.width,
|
|
107
|
+
height: size.height
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function clamp({ value, least, most }: {
|
|
112
|
+
value: number;
|
|
113
|
+
least: number;
|
|
114
|
+
most: number;
|
|
115
|
+
}) {
|
|
116
|
+
return Math.min(Math.max(value, least), most);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* The bounds a window sized to its content takes: the sides `mode` names go to
|
|
121
|
+
* `desired` (what the content asks for, frame included) within `limits`, and
|
|
122
|
+
* the `anchor` corner stays. null when the window is already that size, so a
|
|
123
|
+
* caller makes no call for it.
|
|
124
|
+
*/
|
|
125
|
+
export function fittedBounds({ bounds, desired, mode, anchor, limits }: {
|
|
126
|
+
bounds: WindowBounds;
|
|
127
|
+
desired: WindowSize;
|
|
128
|
+
mode: SizeToContent;
|
|
129
|
+
anchor: WindowAnchor;
|
|
130
|
+
limits: WindowLimits;
|
|
131
|
+
}) {
|
|
132
|
+
const isWidthFitted = mode === SizeToContent.width || mode === SizeToContent.both;
|
|
133
|
+
const isHeightFitted = mode === SizeToContent.height || mode === SizeToContent.both;
|
|
134
|
+
const size = {
|
|
135
|
+
width: isWidthFitted ? clamp({
|
|
136
|
+
value: desired.width,
|
|
137
|
+
least: limits.minWidth,
|
|
138
|
+
most: limits.maxWidth
|
|
139
|
+
}) : bounds.width,
|
|
140
|
+
height: isHeightFitted ? clamp({
|
|
141
|
+
value: desired.height,
|
|
142
|
+
least: limits.minHeight,
|
|
143
|
+
most: limits.maxHeight
|
|
144
|
+
}) : bounds.height
|
|
145
|
+
};
|
|
146
|
+
const isUnchanged = size.width === bounds.width && size.height === bounds.height;
|
|
147
|
+
if (isUnchanged) {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return anchoredBounds({
|
|
152
|
+
bounds,
|
|
153
|
+
size,
|
|
154
|
+
anchor
|
|
155
|
+
});
|
|
156
|
+
}
|
package/src/window/backdrop.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AcrylicVariant, attachAcrylic } from "./acrylic-controller.ts";
|
|
2
|
+
import { attachTransparentBackdrop } from "./transparent-backdrop.ts";
|
|
2
3
|
import { write } from "#src/renderer/host/reflect.js";
|
|
3
4
|
import type { FrameworkElement, Window } from "#src/winui.js";
|
|
4
5
|
import { DesktopAcrylicBackdrop, MicaBackdrop, MicaKind } from "#src/winui.js";
|
|
@@ -7,7 +8,9 @@ import { DesktopAcrylicBackdrop, MicaBackdrop, MicaKind } from "#src/winui.js";
|
|
|
7
8
|
* The material behind a window's content. Acrylic blurs and tints what is
|
|
8
9
|
* behind the window (`acrylicBase` and `acrylicThin` are its other two
|
|
9
10
|
* variants, made by the system backdrop controller); Mica tints with the desktop wallpaper (Mica Alt more
|
|
10
|
-
* strongly, as a tabbed title bar has it);
|
|
11
|
+
* strongly, as a tabbed title bar has it); transparent shows the desktop
|
|
12
|
+
* through every pixel the content leaves clear (with per-pixel alpha; pair it
|
|
13
|
+
* with `isBorderless`); none leaves the theme's solid color.
|
|
11
14
|
*/
|
|
12
15
|
export const Backdrop = {
|
|
13
16
|
acrylic: "acrylic",
|
|
@@ -15,7 +18,8 @@ export const Backdrop = {
|
|
|
15
18
|
acrylicThin: "acrylicThin",
|
|
16
19
|
mica: "mica",
|
|
17
20
|
micaAlt: "micaAlt",
|
|
18
|
-
none: "none"
|
|
21
|
+
none: "none",
|
|
22
|
+
transparent: "transparent"
|
|
19
23
|
} as const;
|
|
20
24
|
|
|
21
25
|
/** One of `Backdrop`'s values. */
|
|
@@ -64,6 +68,7 @@ export function applyBackdrop({ window, content, backdrop }: {
|
|
|
64
68
|
}) {
|
|
65
69
|
const variant = controllerVariants.get(backdrop);
|
|
66
70
|
const isControlled = variant !== undefined;
|
|
71
|
+
const isTransparent = backdrop === Backdrop.transparent;
|
|
67
72
|
const systemBackdrop = isControlled ? null : systemBackdropFor(backdrop);
|
|
68
73
|
const isRemoved = systemBackdrop === null;
|
|
69
74
|
if (isRemoved) {
|
|
@@ -74,6 +79,10 @@ export function applyBackdrop({ window, content, backdrop }: {
|
|
|
74
79
|
window.systemBackdrop = systemBackdrop;
|
|
75
80
|
}
|
|
76
81
|
|
|
82
|
+
if (isTransparent) {
|
|
83
|
+
return attachTransparentBackdrop(window);
|
|
84
|
+
}
|
|
85
|
+
|
|
77
86
|
if (isControlled) {
|
|
78
87
|
return attachAcrylic({
|
|
79
88
|
window,
|
|
@@ -21,6 +21,8 @@ interface WindowChrome extends Pick<
|
|
|
21
21
|
| "isShownInSwitchers"
|
|
22
22
|
| "isToolWindow"
|
|
23
23
|
| "owner"
|
|
24
|
+
| "anchor"
|
|
25
|
+
| "sizeToContent"
|
|
24
26
|
| "title"
|
|
25
27
|
> {
|
|
26
28
|
/** In device pixels. */
|
|
@@ -123,7 +125,9 @@ export function createWindow<Props extends object>(options: WindowOptions<Props>
|
|
|
123
125
|
isExcludedFromCapture: options.isExcludedFromCapture,
|
|
124
126
|
isHidden: options.isHidden,
|
|
125
127
|
isShownInSwitchers: options.isShownInSwitchers,
|
|
126
|
-
isToolWindow: options.isToolWindow
|
|
128
|
+
isToolWindow: options.isToolWindow,
|
|
129
|
+
sizeToContent: options.sizeToContent,
|
|
130
|
+
anchor: options.anchor
|
|
127
131
|
},
|
|
128
132
|
titleBar: () => null,
|
|
129
133
|
size: options.size
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Device-independent pixels (what the web calls CSS pixels) as device pixels,
|
|
3
|
+
* at `scale` device pixels per device-independent pixel.
|
|
4
|
+
*/
|
|
5
|
+
export function toDevicePixels({ value, scale }: {
|
|
6
|
+
value: number;
|
|
7
|
+
scale: number;
|
|
8
|
+
}) {
|
|
9
|
+
return Math.round(value * scale);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** Device pixels as device-independent (CSS) pixels, at `scale` device pixels per one. */
|
|
13
|
+
export function toCssPixels({ value, scale }: {
|
|
14
|
+
value: number;
|
|
15
|
+
scale: number;
|
|
16
|
+
}) {
|
|
17
|
+
return Math.round(value / scale);
|
|
18
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { WindowAnchor } from "./anchor.ts";
|
|
2
|
+
import { dockedBounds } from "./anchor.ts";
|
|
3
|
+
import { foregroundWindowHandle } from "./native.ts";
|
|
4
|
+
import type { AppWindow } from "#src/winui.js";
|
|
5
|
+
import { DisplayArea, DisplayAreaFallback } from "#src/winui.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Which display a window is docked to: the one the user is working on (the
|
|
9
|
+
* foreground window's), the one the window is on, or the primary one.
|
|
10
|
+
*/
|
|
11
|
+
export const DockDisplay = {
|
|
12
|
+
active: "active",
|
|
13
|
+
current: "current",
|
|
14
|
+
primary: "primary"
|
|
15
|
+
} as const;
|
|
16
|
+
|
|
17
|
+
/** One of `DockDisplay`'s values. */
|
|
18
|
+
export type DockDisplay = (typeof DockDisplay)[keyof typeof DockDisplay];
|
|
19
|
+
|
|
20
|
+
const NO_WINDOW = 0n;
|
|
21
|
+
|
|
22
|
+
function activeDisplayArea(appWindow: AppWindow) {
|
|
23
|
+
const foreground = foregroundWindowHandle();
|
|
24
|
+
const hasForeground = foreground !== NO_WINDOW;
|
|
25
|
+
const windowId = hasForeground ? { value: foreground } : appWindow.id;
|
|
26
|
+
return DisplayArea.getFromWindowId(windowId, DisplayAreaFallback.Nearest);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const DISPLAY_FINDERS = {
|
|
30
|
+
[DockDisplay.active]: activeDisplayArea,
|
|
31
|
+
[DockDisplay.current]: appWindow => DisplayArea.getFromWindowId(appWindow.id, DisplayAreaFallback.Nearest),
|
|
32
|
+
[DockDisplay.primary]: () => DisplayArea.primary
|
|
33
|
+
} as const satisfies Record<DockDisplay, (appWindow: AppWindow) => DisplayArea>;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* The display area `display` names for `appWindow`. The `active` one is the
|
|
37
|
+
* display of the foreground window, so an overlay that shows up while the user
|
|
38
|
+
* works on a second monitor lands there.
|
|
39
|
+
*
|
|
40
|
+
* @param options - The window and which display to find.
|
|
41
|
+
* @returns The WinUI `DisplayArea`: `workArea` is its desktop minus the taskbar, in device pixels.
|
|
42
|
+
*/
|
|
43
|
+
export function displayAreaOf({ appWindow, display }: {
|
|
44
|
+
appWindow: AppWindow;
|
|
45
|
+
display: DockDisplay;
|
|
46
|
+
}) {
|
|
47
|
+
return DISPLAY_FINDERS[display](appWindow);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Where `dockWindow` puts a window. */
|
|
51
|
+
export interface DockOptions {
|
|
52
|
+
/** The corner of the work area the window sits against. */
|
|
53
|
+
anchor: WindowAnchor;
|
|
54
|
+
|
|
55
|
+
/** Device pixels between the window and the two edges at that corner; 0 by default. */
|
|
56
|
+
inset?: number;
|
|
57
|
+
|
|
58
|
+
/** The display to dock to; the active one by default. */
|
|
59
|
+
display?: DockDisplay;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Moves the window against the `anchor` corner of a display's work area (the
|
|
64
|
+
* display minus the taskbar), `inset` device pixels in from both edges, with
|
|
65
|
+
* one `AppWindow.MoveAndResize`. The window keeps its size.
|
|
66
|
+
*
|
|
67
|
+
* A window moved to a display with another scale first goes there at its old
|
|
68
|
+
* size and is docked once Windows has rescaled it, so the corner it ends on is
|
|
69
|
+
* the one asked for at the size it really has.
|
|
70
|
+
*
|
|
71
|
+
* @param options - The window, the corner, the inset in device pixels (0 by
|
|
72
|
+
* default) and the display (the active one by default).
|
|
73
|
+
*/
|
|
74
|
+
export function dockWindow({ appWindow, anchor, inset = 0, display = DockDisplay.active }: DockOptions & {
|
|
75
|
+
appWindow: AppWindow;
|
|
76
|
+
}) {
|
|
77
|
+
const area = displayAreaOf({
|
|
78
|
+
appWindow,
|
|
79
|
+
display
|
|
80
|
+
});
|
|
81
|
+
const { workArea } = area;
|
|
82
|
+
const currentArea = DisplayArea.getFromWindowId(appWindow.id, DisplayAreaFallback.Nearest);
|
|
83
|
+
const isOnOtherDisplay = currentArea.displayId.value !== area.displayId.value;
|
|
84
|
+
if (isOnOtherDisplay) {
|
|
85
|
+
appWindow.move({
|
|
86
|
+
x: workArea.x,
|
|
87
|
+
y: workArea.y
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
appWindow.moveAndResize(
|
|
92
|
+
dockedBounds({
|
|
93
|
+
area: workArea,
|
|
94
|
+
size: appWindow.size,
|
|
95
|
+
anchor,
|
|
96
|
+
inset
|
|
97
|
+
})
|
|
98
|
+
);
|
|
99
|
+
}
|
package/src/window/index.ts
CHANGED
|
@@ -2,6 +2,10 @@ export type { RunWindowOptions } from "./application.ts";
|
|
|
2
2
|
export { exitApplication, runWindow } from "./application.ts";
|
|
3
3
|
export type { AppWindow, WindowOptions } from "./create-window.ts";
|
|
4
4
|
export { Backdrop } from "./backdrop.ts";
|
|
5
|
+
export type { WindowBounds, WindowSize } from "./anchor.ts";
|
|
6
|
+
export { SizeToContent, WindowAnchor } from "./anchor.ts";
|
|
7
|
+
export type { DockOptions } from "./dock.ts";
|
|
8
|
+
export { displayAreaOf, DockDisplay, dockWindow } from "./dock.ts";
|
|
5
9
|
export type { CaptionButtonColors, WindowRegion } from "./chrome.ts";
|
|
6
10
|
export { TitleBarHeight, WindowPresentation, WindowTheme } from "./chrome.ts";
|
|
7
11
|
export { createWindow } from "./create-window.ts";
|
package/src/window/native.ts
CHANGED
|
@@ -58,6 +58,18 @@ export function scaleOf(hwnd: bigint) {
|
|
|
58
58
|
return dpi > 0 ? dpi / BASE_DPI : 1;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
const getForegroundWindow = bind({
|
|
62
|
+
dll: "user32.dll",
|
|
63
|
+
entryPoint: "GetForegroundWindow",
|
|
64
|
+
parameters: [],
|
|
65
|
+
returnType: "pointer"
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
/** The handle of the window the user is working in; 0 when no window has the foreground. */
|
|
69
|
+
export function foregroundWindowHandle() {
|
|
70
|
+
return addressOf(getForegroundWindow.invoke([]).returnValue);
|
|
71
|
+
}
|
|
72
|
+
|
|
61
73
|
/**
|
|
62
74
|
* Makes the window `owner` own the window `hwnd`: the owned window stays above
|
|
63
75
|
* its owner, and closes, minimizes and restores with it.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { write } from "#src/renderer/host/reflect.js";
|
|
2
|
+
import type { Window } from "#src/winui.js";
|
|
3
|
+
import { ICompositionSupportsSystemBackdrop, SystemCompositor } from "#src/winui.js";
|
|
4
|
+
|
|
5
|
+
// The Windows App SDK's own Microsoft.UI.Xaml.Media.TransparentTintBackdrop is
|
|
6
|
+
// not in the metadata of any release this package restores (2.2 and 2.3 have
|
|
7
|
+
// DesktopAcrylicBackdrop, MicaBackdrop and the abstract SystemBackdrop only),
|
|
8
|
+
// so it cannot be generated. What it does is what a window offers through
|
|
9
|
+
// ICompositionSupportsSystemBackdrop: a composition brush placed behind the
|
|
10
|
+
// content, one of the system's (Windows.UI.Composition) and not of the window's
|
|
11
|
+
// own compositor (Microsoft.UI.Composition), which is what the interface takes.
|
|
12
|
+
// A color brush with alpha 0 there makes the window show the desktop through
|
|
13
|
+
// every pixel the content leaves clear, with per-pixel alpha.
|
|
14
|
+
|
|
15
|
+
const CLEAR = {
|
|
16
|
+
a: 0,
|
|
17
|
+
r: 0,
|
|
18
|
+
g: 0,
|
|
19
|
+
b: 0
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Puts a fully transparent system backdrop behind `window`'s content: the
|
|
24
|
+
* desktop shows through wherever the content paints nothing, and a partly
|
|
25
|
+
* transparent pixel blends with it. The window must have no XAML backdrop of
|
|
26
|
+
* its own (`applyBackdrop` takes it away first).
|
|
27
|
+
*
|
|
28
|
+
* @returns What takes the backdrop away again.
|
|
29
|
+
*/
|
|
30
|
+
export function attachTransparentBackdrop(window: Window) {
|
|
31
|
+
const target = window.as(ICompositionSupportsSystemBackdrop);
|
|
32
|
+
target.systemBackdrop = new SystemCompositor().createColorBrush(CLEAR);
|
|
33
|
+
return () => {
|
|
34
|
+
// The binding types the property as non-null, but null is how the target
|
|
35
|
+
// lets a backdrop go.
|
|
36
|
+
write(target, "systemBackdrop", null);
|
|
37
|
+
};
|
|
38
|
+
}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { applyShownInSwitchers } from "./apply-chrome.ts";
|
|
2
|
+
import type { DockOptions } from "./dock.ts";
|
|
3
|
+
import { dockWindow } from "./dock.ts";
|
|
2
4
|
import { scaleOf } from "./native.ts";
|
|
3
5
|
import type { OpenFilePickerOptions, PickerOptions, SaveFilePickerOptions } from "./pickers.ts";
|
|
4
6
|
import { pickFile, pickFilePaths, pickFolder, saveFile } from "./pickers.ts";
|
|
7
|
+
import { WindowGeometry } from "#src/web-apis/window-geometry.js";
|
|
5
8
|
import type { Window } from "#src/winui.js";
|
|
6
9
|
import {
|
|
7
10
|
FileOpenPicker,
|
|
@@ -18,11 +21,18 @@ import {
|
|
|
18
21
|
export class WindowHandle {
|
|
19
22
|
/** The native WinUI window. */
|
|
20
23
|
readonly window: Window;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The window's size and place as the web has them: `outerWidth`,
|
|
27
|
+
* `screenX`, `resizeTo`, `moveBy` and the rest, in CSS pixels.
|
|
28
|
+
*/
|
|
29
|
+
readonly geometry: WindowGeometry;
|
|
21
30
|
private isClosed = false;
|
|
22
31
|
private closeGuard = () => false;
|
|
23
32
|
|
|
24
33
|
constructor(window: Window) {
|
|
25
34
|
this.window = window;
|
|
35
|
+
this.geometry = new WindowGeometry(window.appWindow, () => this.scale);
|
|
26
36
|
window.onClosed(() => {
|
|
27
37
|
this.isClosed = true;
|
|
28
38
|
});
|
|
@@ -59,6 +69,22 @@ export class WindowHandle {
|
|
|
59
69
|
});
|
|
60
70
|
}
|
|
61
71
|
|
|
72
|
+
/**
|
|
73
|
+
* Moves the window against a corner of a display's work area (the display
|
|
74
|
+
* minus the taskbar), `inset` device pixels in from both edges: an overlay
|
|
75
|
+
* that sits bottom-right above the taskbar of the display the user is on.
|
|
76
|
+
* With `sizeToContent` and the same `anchor` on the window, the corner stays
|
|
77
|
+
* as the content grows.
|
|
78
|
+
*
|
|
79
|
+
* @param options - The corner, the inset in device pixels and the display (the active one by default).
|
|
80
|
+
*/
|
|
81
|
+
dock(options: DockOptions) {
|
|
82
|
+
dockWindow({
|
|
83
|
+
appWindow: this.appWindow,
|
|
84
|
+
...options
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
62
88
|
/** Shows the window and gives it focus. */
|
|
63
89
|
activate() {
|
|
64
90
|
this.window.activate();
|