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.
Files changed (108) hide show
  1. package/API.md +120 -6
  2. package/README.md +70 -90
  3. package/dist/bindings/.dynwinrt-managed +1 -1
  4. package/dist/components/convert.d.ts +3 -3
  5. package/dist/components/convert.d.ts.map +1 -1
  6. package/dist/components/fluent-glyph.d.ts +4 -0
  7. package/dist/components/fluent-glyph.d.ts.map +1 -1
  8. package/dist/components/generated/controls/FontIcon.svelte.d.ts +1 -1
  9. package/dist/components/generated/controls/FontIconSource.svelte.d.ts +1 -1
  10. package/dist/components/generated/controls/UIElement.svelte.d.ts +1 -1
  11. package/dist/components/index.d.ts +4 -2
  12. package/dist/components/index.d.ts.map +1 -1
  13. package/dist/components/index.js.map +1 -1
  14. package/dist/css/runtime/resources.d.ts +1 -1
  15. package/dist/dev/dev-window.d.ts +23 -0
  16. package/dist/dev/dev-window.d.ts.map +1 -0
  17. package/dist/dev/dev-window.js +42 -0
  18. package/dist/dev/dev-window.js.map +1 -0
  19. package/dist/dev/index.d.ts +3 -0
  20. package/dist/dev/index.d.ts.map +1 -0
  21. package/dist/dev/index.js +2 -0
  22. package/dist/dev/index.js.map +1 -0
  23. package/dist/dev/start-dev.d.ts +47 -0
  24. package/dist/dev/start-dev.d.ts.map +1 -0
  25. package/dist/dev/start-dev.js +81 -0
  26. package/dist/dev/start-dev.js.map +1 -0
  27. package/dist/enums.d.ts +2 -0
  28. package/dist/enums.d.ts.map +1 -1
  29. package/dist/enums.js +2 -0
  30. package/dist/enums.js.map +1 -1
  31. package/dist/media/media-player.d.ts +8 -0
  32. package/dist/media/media-player.d.ts.map +1 -1
  33. package/dist/media/media-player.js +15 -0
  34. package/dist/media/media-player.js.map +1 -1
  35. package/dist/web-apis/window-geometry.d.ts +39 -0
  36. package/dist/web-apis/window-geometry.d.ts.map +1 -0
  37. package/dist/web-apis/window-geometry.js +95 -0
  38. package/dist/web-apis/window-geometry.js.map +1 -0
  39. package/dist/win2d/Canvas.svelte +1 -0
  40. package/dist/window/anchor.d.ts +92 -0
  41. package/dist/window/anchor.d.ts.map +1 -0
  42. package/dist/window/anchor.js +100 -0
  43. package/dist/window/anchor.js.map +1 -0
  44. package/dist/window/backdrop.d.ts +4 -1
  45. package/dist/window/backdrop.d.ts.map +1 -1
  46. package/dist/window/backdrop.js +10 -2
  47. package/dist/window/backdrop.js.map +1 -1
  48. package/dist/window/create-window.d.ts +1 -1
  49. package/dist/window/create-window.d.ts.map +1 -1
  50. package/dist/window/create-window.js +3 -1
  51. package/dist/window/create-window.js.map +1 -1
  52. package/dist/window/css-pixels.d.ts +14 -0
  53. package/dist/window/css-pixels.d.ts.map +1 -0
  54. package/dist/window/css-pixels.js +12 -0
  55. package/dist/window/css-pixels.js.map +1 -0
  56. package/dist/window/dock.d.ts +51 -0
  57. package/dist/window/dock.d.ts.map +1 -0
  58. package/dist/window/dock.js +69 -0
  59. package/dist/window/dock.js.map +1 -0
  60. package/dist/window/index.d.ts +4 -0
  61. package/dist/window/index.d.ts.map +1 -1
  62. package/dist/window/index.js +2 -0
  63. package/dist/window/index.js.map +1 -1
  64. package/dist/window/native.d.ts +2 -0
  65. package/dist/window/native.d.ts.map +1 -1
  66. package/dist/window/native.js +10 -0
  67. package/dist/window/native.js.map +1 -1
  68. package/dist/window/transparent-backdrop.d.ts +11 -0
  69. package/dist/window/transparent-backdrop.d.ts.map +1 -0
  70. package/dist/window/transparent-backdrop.js +35 -0
  71. package/dist/window/transparent-backdrop.js.map +1 -0
  72. package/dist/window/window-handle.d.ts +17 -0
  73. package/dist/window/window-handle.d.ts.map +1 -1
  74. package/dist/window/window-handle.js +23 -0
  75. package/dist/window/window-handle.js.map +1 -1
  76. package/dist/window/window-host.svelte.d.ts +17 -12
  77. package/dist/window/window-host.svelte.d.ts.map +1 -1
  78. package/dist/window/window-host.svelte.js +208 -23
  79. package/dist/window/window-host.svelte.js.map +1 -1
  80. package/dist/winui.d.ts +5 -0
  81. package/dist/winui.d.ts.map +1 -1
  82. package/dist/winui.js +5 -0
  83. package/dist/winui.js.map +1 -1
  84. package/package.json +25 -7
  85. package/src/components/convert.ts +3 -3
  86. package/src/components/fluent-glyph.ts +6 -0
  87. package/src/components/generated/controls/FontIcon.svelte.ts +1 -1
  88. package/src/components/generated/controls/FontIconSource.svelte.ts +1 -1
  89. package/src/components/generated/controls/UIElement.svelte.ts +1 -1
  90. package/src/components/index.ts +13 -2
  91. package/src/dev/dev-window.ts +63 -0
  92. package/src/dev/index.ts +2 -0
  93. package/src/dev/start-dev.ts +120 -0
  94. package/src/enums.ts +2 -0
  95. package/src/media/media-player.ts +17 -0
  96. package/src/web-apis/window-geometry.ts +106 -0
  97. package/src/window/anchor.ts +156 -0
  98. package/src/window/backdrop.ts +11 -2
  99. package/src/window/create-window.ts +5 -1
  100. package/src/window/css-pixels.ts +18 -0
  101. package/src/window/dock.ts +99 -0
  102. package/src/window/index.ts +4 -0
  103. package/src/window/native.ts +12 -0
  104. package/src/window/transparent-backdrop.ts +38 -0
  105. package/src/window/window-handle.ts +26 -0
  106. package/src/window/window-host.svelte.ts +256 -36
  107. package/src/winui.ts +5 -0
  108. 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
+ }
@@ -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); none leaves the theme's solid color.
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
+ }
@@ -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";
@@ -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();