Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bb97110886 | |||
| 7379550a27 |
@@ -484,7 +484,7 @@ For timestamps, the facade owns the clocks (Zig 0.16 puts `std.time` behind `std
|
||||
|
||||
## Secondary windows
|
||||
|
||||
In a default TypeScript app, export `windows(model): readonly WindowDescriptor[]` and put each possible window's markup at `src/windows/<label>.native`; the generated launcher compiles those files, maps descriptor labels to them, and includes shared components they import from nested paths under `src/windows/`. Construct descriptors with `windowDescriptor`. `closePolicy: "quit"` routes `onCloseCommand` through `commandMsg`; `"hide"` retains the window and dispatches no close command. `titlebar` includes `"chromeless"` for fully skinned windows. `examples/system-monitor-ts` is the complete default-path reference.
|
||||
In a default TypeScript app, export `windows(model): readonly WindowDescriptor[]` and put each possible window's markup at `src/windows/<label>.native`; the generated launcher compiles those files, maps descriptor labels to them, and includes shared components they import from nested paths under `src/windows/`. Construct descriptors with `windowDescriptor`. `restorePolicy: "center_on_primary"` centers a fresh descriptor with no `x`/`y` on macOS; Windows and Linux currently keep their native default placement. The default policy is `"clamp_to_visible_screen"`. `closePolicy: "quit"` routes `onCloseCommand` through `commandMsg`; `"hide"` retains the window and dispatches no close command. `titlebar` includes `"chromeless"` for fully skinned windows. `examples/system-monitor-ts` is the complete default-path reference.
|
||||
|
||||
In Zig cores and custom wiring, windows are model-declared exactly like the tray: `Options.windows_fn` returns the window descriptors that should exist right now (presence IS liveness), and `Options.window_view` builds each declared window's whole canvas tree, keyed by the descriptor's window label. After every dispatched Msg the runtime reconciles: windows the model started declaring are created (a source-less native window wearing one `gpu_surface` view with the descriptor's `canvas_label`, inheriting the main canvas's gpu options), windows it stopped declaring close, and every open window's view rebuilds from the same model — a theme picked in the settings window restyles the main window on the same dispatch. There is no `visible` flag: transient visibility is host state changed through `hideWindow`/`showWindow` or a `.hide` close policy; stop declaring a window to really close it and release its retained views.
|
||||
|
||||
|
||||
@@ -482,6 +482,8 @@ Export `windows(model): readonly WindowDescriptor[]` to derive the live secondar
|
||||
|
||||
`closePolicy` accepts `"quit"` (the default) or `"hide"`. A `"quit"` user close routes `onCloseCommand` through `commandMsg`, where the app maps it to the Msg that clears its open flag. A `"hide"` close retains the same native window and view and dispatches no close command; `Cmd.showWindow(label)` reveals it. Model-declared secondary windows are desktop-only. See `examples/system-monitor-ts`.
|
||||
|
||||
`restorePolicy` accepts `"clamp_to_visible_screen"` (the default) or `"center_on_primary"`. Model-declared windows do not restore persisted frames. On macOS, `"center_on_primary"` centers a fresh descriptor with no authored `x`/`y`; Windows and Linux currently keep their native default placement.
|
||||
|
||||
`titlebar` accepts `"standard"`, `"hidden_inset"`, `"hidden_inset_tall"`, or `"chromeless"`. Transparent Windows windows require `"chromeless"`; because that removes the system buttons, fully skinned windows must draw working close/minimize controls.
|
||||
|
||||
## Subscriptions are Sub data
|
||||
|
||||
@@ -96,6 +96,7 @@ export function windowDescriptor(spec: WindowDescriptorSpec): WindowDescriptor {
|
||||
x: spec.x ?? null,
|
||||
y: spec.y ?? null,
|
||||
resizable: spec.resizable ?? true,
|
||||
restorePolicy: spec.restorePolicy ?? "clamp_to_visible_screen",
|
||||
minWidth: spec.minWidth ?? 0,
|
||||
minHeight: spec.minHeight ?? 0,
|
||||
titlebar: spec.titlebar ?? "standard",
|
||||
|
||||
@@ -388,6 +388,7 @@ export function windowDescriptor(spec: WindowDescriptorSpec): WindowDescriptor {
|
||||
x: spec.x ?? null,
|
||||
y: spec.y ?? null,
|
||||
resizable: spec.resizable ?? true,
|
||||
restorePolicy: spec.restorePolicy ?? "clamp_to_visible_screen",
|
||||
minWidth: spec.minWidth ?? 0,
|
||||
minHeight: spec.minHeight ?? 0,
|
||||
titlebar: spec.titlebar ?? "standard",
|
||||
|
||||
Vendored
+3
@@ -55,6 +55,7 @@ export interface StatusItemDescriptor {
|
||||
}
|
||||
export type WindowClosePolicy = "quit" | "hide";
|
||||
export type WindowTitlebarStyle = "standard" | "hidden_inset" | "hidden_inset_tall" | "chromeless";
|
||||
export type WindowRestorePolicy = "clamp_to_visible_screen" | "center_on_primary";
|
||||
export interface WindowDescriptorSpec {
|
||||
readonly label: Uint8Array;
|
||||
readonly canvasLabel: Uint8Array;
|
||||
@@ -64,6 +65,7 @@ export interface WindowDescriptorSpec {
|
||||
readonly x?: number | null;
|
||||
readonly y?: number | null;
|
||||
readonly resizable?: boolean;
|
||||
readonly restorePolicy?: WindowRestorePolicy;
|
||||
readonly minWidth?: number;
|
||||
readonly minHeight?: number;
|
||||
readonly titlebar?: WindowTitlebarStyle;
|
||||
@@ -84,6 +86,7 @@ export interface WindowDescriptor {
|
||||
readonly x: number | null;
|
||||
readonly y: number | null;
|
||||
readonly resizable: boolean;
|
||||
readonly restorePolicy: WindowRestorePolicy;
|
||||
readonly minWidth: number;
|
||||
readonly minHeight: number;
|
||||
readonly titlebar: WindowTitlebarStyle;
|
||||
|
||||
@@ -135,6 +135,8 @@ export type WindowClosePolicy = "quit" | "hide";
|
||||
|
||||
export type WindowTitlebarStyle = "standard" | "hidden_inset" | "hidden_inset_tall" | "chromeless";
|
||||
|
||||
export type WindowRestorePolicy = "clamp_to_visible_screen" | "center_on_primary";
|
||||
|
||||
/// Author-facing input to `windowDescriptor`; omitted fields receive the
|
||||
/// same defaults as UiApp.WindowDescriptor.
|
||||
export interface WindowDescriptorSpec {
|
||||
@@ -146,6 +148,7 @@ export interface WindowDescriptorSpec {
|
||||
readonly x?: number | null;
|
||||
readonly y?: number | null;
|
||||
readonly resizable?: boolean;
|
||||
readonly restorePolicy?: WindowRestorePolicy;
|
||||
readonly minWidth?: number;
|
||||
readonly minHeight?: number;
|
||||
readonly titlebar?: WindowTitlebarStyle;
|
||||
@@ -172,6 +175,7 @@ export interface WindowDescriptor {
|
||||
readonly x: number | null;
|
||||
readonly y: number | null;
|
||||
readonly resizable: boolean;
|
||||
readonly restorePolicy: WindowRestorePolicy;
|
||||
readonly minWidth: number;
|
||||
readonly minHeight: number;
|
||||
readonly titlebar: WindowTitlebarStyle;
|
||||
|
||||
@@ -1467,12 +1467,13 @@ export class SubsetChecker {
|
||||
return candidate?.type.k === "optional" && ["number", "i64", "f64", "numAlias"].includes(candidate.type.inner.k);
|
||||
};
|
||||
const valid =
|
||||
names.join(",") === "activateOnShow,allowsFullscreen,alwaysOnTop,canvasLabel,clickThrough,closePolicy,height,label,minHeight,minWidth,onCloseCommand,resizable,title,titlebar,transparent,width,x,y" &&
|
||||
names.join(",") === "activateOnShow,allowsFullscreen,alwaysOnTop,canvasLabel,clickThrough,closePolicy,height,label,minHeight,minWidth,onCloseCommand,resizable,restorePolicy,title,titlebar,transparent,width,x,y" &&
|
||||
field("label")?.type.k === "bytes" &&
|
||||
field("canvasLabel")?.type.k === "bytes" &&
|
||||
field("title")?.type.k === "bytes" &&
|
||||
numeric("width") && numeric("height") && optionalNumeric("x") && optionalNumeric("y") &&
|
||||
field("resizable")?.type.k === "bool" && numeric("minWidth") && numeric("minHeight") &&
|
||||
field("resizable")?.type.k === "bool" && enumMembersAre("restorePolicy", ["clamp_to_visible_screen", "center_on_primary"]) &&
|
||||
numeric("minWidth") && numeric("minHeight") &&
|
||||
enumMembersAre("titlebar", ["standard", "hidden_inset", "hidden_inset_tall", "chromeless"]) &&
|
||||
field("transparent")?.type.k === "bool" && field("alwaysOnTop")?.type.k === "bool" &&
|
||||
field("clickThrough")?.type.k === "bool" && field("activateOnShow")?.type.k === "bool" &&
|
||||
|
||||
@@ -34,6 +34,7 @@ test("windowDescriptor fills canonical window defaults", () => {
|
||||
assert.equal(descriptor.width, 480);
|
||||
assert.equal(descriptor.height, 360);
|
||||
assert.equal(descriptor.resizable, true);
|
||||
assert.equal(descriptor.restorePolicy, "clamp_to_visible_screen");
|
||||
assert.equal(descriptor.titlebar, "chromeless");
|
||||
assert.equal(descriptor.closePolicy, "hide");
|
||||
assert.equal(descriptor.onCloseCommand.length, 0);
|
||||
|
||||
@@ -1341,7 +1341,7 @@ export function initialModel(): Model { return { settingsOpen: false }; }
|
||||
export function update(model: Model, msg: Msg): Model { return model; }
|
||||
export function windows(model: Model): readonly WindowDescriptor[] {
|
||||
if (!model.settingsOpen) return [];
|
||||
return [windowDescriptor({ label: asciiBytes("settings"), canvasLabel: asciiBytes("settings-canvas"), titlebar: "chromeless", transparent: true, closePolicy: "hide", onCloseCommand: asciiBytes("settings.closed") })];
|
||||
return [windowDescriptor({ label: asciiBytes("settings"), canvasLabel: asciiBytes("settings-canvas"), restorePolicy: "center_on_primary", titlebar: "chromeless", transparent: true, closePolicy: "hide", onCloseCommand: asciiBytes("settings.closed") })];
|
||||
}
|
||||
`;
|
||||
const clean = check(cleanSource, { windowViews: ["settings"] });
|
||||
|
||||
@@ -878,7 +878,7 @@ The `.wake` platform event is how live platforms marshal worker completions onto
|
||||
|
||||
## Secondary windows
|
||||
|
||||
In the default TypeScript path, export `windows(model): readonly WindowDescriptor[]` and put each possible window's Native markup at `src/windows/<label>.native`. Spell each constructor identity as a literal `label: asciiBytes("<label>")`; `native check` and every build reject dynamic labels and missing roots. Those roots may import shared components nested under `src/windows/`; the generated launcher compiles and hot-reloads the full closure. Use `windowDescriptor` from `@native-sdk/core`; its `closePolicy` field accepts `"quit"` or `"hide"`, and `titlebar` includes `"chromeless"`. A `"quit"` user close routes `onCloseCommand` through `commandMsg`; a `"hide"` close preserves the window and view and dispatches no close command. The `ts-core` skill has the complete descriptor and example.
|
||||
In the default TypeScript path, export `windows(model): readonly WindowDescriptor[]` and put each possible window's Native markup at `src/windows/<label>.native`. Spell each constructor identity as a literal `label: asciiBytes("<label>")`; `native check` and every build reject dynamic labels and missing roots. Those roots may import shared components nested under `src/windows/`; the generated launcher compiles and hot-reloads the full closure. Use `windowDescriptor` from `@native-sdk/core`; its `restorePolicy` field accepts `"clamp_to_visible_screen"` or `"center_on_primary"` (centering is currently macOS-only), `closePolicy` accepts `"quit"` or `"hide"`, and `titlebar` includes `"chromeless"`. A `"quit"` user close routes `onCloseCommand` through `commandMsg`; a `"hide"` close preserves the window and view and dispatches no close command. The `ts-core` skill has the complete descriptor and example.
|
||||
|
||||
### Zig cores: `windows_fn` + `window_view`
|
||||
|
||||
@@ -905,6 +905,7 @@ fn windowView(ui: *App.Ui, model: *const Model, window_label: []const u8) App.Ui
|
||||
Rules that matter:
|
||||
- **Every canvas label must be unique across the app** (main + declared windows); input routes back by it, and automation verbs (`widget-click <canvas-label> <id>`, `screenshot`) address any window's canvas the same way.
|
||||
- **Close policy**: `WindowDescriptor.close_policy` is `.quit` by default; a user close really closes and dispatches `on_close` (the dismissal precedent), so clear the open flag in `update` — or keep declaring the window and the next rebuild brings it back (source wins). `.hide` keeps the same window, slot, and views alive, dispatches no `on_close`, and `showWindow(label)` reveals it. A close the model itself initiated never echoes a Msg. Existing platform safeguards still apply: unsupported hosts refuse `.hide` rather than strand an unreachable window.
|
||||
- **Restore policy**: `WindowDescriptor.restore_policy` is `.clamp_to_visible_screen` by default. On macOS, `.center_on_primary` centers a fresh descriptor with no authored `x`/`y`; Windows and Linux currently keep their native default placement. Model-declared windows deliberately do not restore persisted frames.
|
||||
- **Budget**: at most `UiApp.max_ui_windows` (4) declared windows; excess warns and is ignored. Every dispatched Msg rebuilds every open window's view.
|
||||
- **Present-before-show**: canvas windows (any `gpu_surface` view — startup, scene, and declared windows alike) are created ordered-out and become visible only after their first canvas frame presents, so opening one never flashes blank. Automatic (`WindowOptions.show = .on_first_present`, derived from the views); webview windows show immediately. The null platform records `window_show`, `window_visible`, and present/shown sequence numbers for ordering assertions; `NATIVE_SDK_WINDOW_TIMING=1` logs create→show latency on macOS.
|
||||
- **Markup binds ONE window's content** — there is no `window` element in the closed grammar. A markup-authored secondary window is a `canvas.CompiledMarkupView` whose `build` `window_view` calls for that label.
|
||||
|
||||
@@ -231,6 +231,7 @@ export function windows(model: Model): readonly WindowDescriptor[] {
|
||||
title: asciiBytes("Settings"),
|
||||
width: 420,
|
||||
height: 240,
|
||||
restorePolicy: "center_on_primary",
|
||||
resizable: false,
|
||||
closePolicy: "quit",
|
||||
onCloseCommand: asciiBytes("app.settings-closed"),
|
||||
@@ -240,6 +241,8 @@ export function windows(model: Model): readonly WindowDescriptor[] {
|
||||
|
||||
`closePolicy` is `"quit"` by default. Under `"quit"`, a user close really closes the window and routes `onCloseCommand` through `commandMsg`; map it to the Msg that clears the model's open flag. If the model keeps declaring the label, source wins and the next reconciliation recreates it. Under `"hide"`, the same native window and view stay alive, no close command fires, and `Cmd.showWindow("settings")` reveals it. Stopping the declaration always performs a real reconcile close. The platform safeguards for `hide` are the same as manifest windows. Model-declared secondary windows are desktop-only.
|
||||
|
||||
`restorePolicy` accepts `"clamp_to_visible_screen"` (the default) or `"center_on_primary"`. Model-declared windows do not restore persisted frames. On macOS, the latter centers a fresh descriptor with no `x`/`y` directly at native creation time; Windows and Linux currently keep their native default placement.
|
||||
|
||||
`titlebar` accepts `"standard"`, `"hidden_inset"`, `"hidden_inset_tall"`, and `"chromeless"`. The last removes all OS chrome and is required when a transparent model-declared window targets Windows; provide working app-drawn close/minimize controls for that fully skinned shape.
|
||||
|
||||
### The init command
|
||||
|
||||
@@ -720,6 +720,7 @@ pub fn TsUiApp(comptime core: type) type {
|
||||
.x = optionalWindowFloat(window.x),
|
||||
.y = optionalWindowFloat(window.y),
|
||||
.resizable = window.resizable,
|
||||
.restore_policy = windowRestorePolicy(window.restorePolicy),
|
||||
.min_width = statusItemFloat(window.minWidth),
|
||||
.min_height = statusItemFloat(window.minHeight),
|
||||
.titlebar = windowTitlebar(window.titlebar),
|
||||
@@ -752,6 +753,14 @@ pub fn TsUiApp(comptime core: type) type {
|
||||
unreachable;
|
||||
}
|
||||
|
||||
fn windowRestorePolicy(value: anytype) @import("app_manifest").WindowRestorePolicy {
|
||||
const Target = @import("app_manifest").WindowRestorePolicy;
|
||||
inline for (std.meta.fields(Target)) |field| {
|
||||
if (std.mem.eql(u8, @tagName(value), field.name)) return @enumFromInt(field.value);
|
||||
}
|
||||
unreachable;
|
||||
}
|
||||
|
||||
fn windowClosePolicy(value: anytype) @import("app_manifest").WindowClosePolicy {
|
||||
const Target = @import("app_manifest").WindowClosePolicy;
|
||||
inline for (std.meta.fields(Target)) |field| {
|
||||
@@ -1018,9 +1027,10 @@ pub fn TsUiApp(comptime core: type) type {
|
||||
if (return_info != .pointer or return_info.pointer.size != .slice or !return_info.pointer.is_const) @compileError(teaching);
|
||||
const Window = statusItemRecordType(return_info.pointer.child, teaching);
|
||||
const info = @typeInfo(Window).@"struct";
|
||||
if (info.fields.len != 18 or !@hasField(Window, "label") or !@hasField(Window, "canvasLabel") or
|
||||
if (info.fields.len != 19 or !@hasField(Window, "label") or !@hasField(Window, "canvasLabel") or
|
||||
!@hasField(Window, "title") or !@hasField(Window, "width") or !@hasField(Window, "height") or
|
||||
!@hasField(Window, "x") or !@hasField(Window, "y") or !@hasField(Window, "resizable") or
|
||||
!@hasField(Window, "restorePolicy") or
|
||||
!@hasField(Window, "minWidth") or !@hasField(Window, "minHeight") or !@hasField(Window, "titlebar") or
|
||||
!@hasField(Window, "transparent") or !@hasField(Window, "alwaysOnTop") or !@hasField(Window, "clickThrough") or
|
||||
!@hasField(Window, "activateOnShow") or !@hasField(Window, "allowsFullscreen") or
|
||||
@@ -1029,6 +1039,7 @@ pub fn TsUiApp(comptime core: type) type {
|
||||
@FieldType(Window, "title") != []const u8 or !statusItemNumericType(@FieldType(Window, "width")) or
|
||||
!statusItemNumericType(@FieldType(Window, "height")) or !optionalNumericType(@FieldType(Window, "x")) or
|
||||
!optionalNumericType(@FieldType(Window, "y")) or @FieldType(Window, "resizable") != bool or
|
||||
!statusItemEnumType(@FieldType(Window, "restorePolicy"), &.{ "clamp_to_visible_screen", "center_on_primary" }) or
|
||||
!statusItemNumericType(@FieldType(Window, "minWidth")) or !statusItemNumericType(@FieldType(Window, "minHeight")) or
|
||||
!statusItemEnumType(@FieldType(Window, "titlebar"), &.{ "standard", "hidden_inset", "hidden_inset_tall", "chromeless" }) or
|
||||
@FieldType(Window, "transparent") != bool or @FieldType(Window, "alwaysOnTop") != bool or
|
||||
@@ -1637,6 +1648,7 @@ test "TypeScript statusItems adapter validates and projects canonical descriptor
|
||||
|
||||
const WindowsAdapterTestCore = struct {
|
||||
const Titlebar = enum { standard, hidden_inset, hidden_inset_tall, chromeless };
|
||||
const RestorePolicy = enum { clamp_to_visible_screen, center_on_primary };
|
||||
const ClosePolicy = enum { quit, hide };
|
||||
const Descriptor = struct {
|
||||
label: []const u8,
|
||||
@@ -1647,6 +1659,7 @@ const WindowsAdapterTestCore = struct {
|
||||
x: ?f64,
|
||||
y: ?f64,
|
||||
resizable: bool,
|
||||
restorePolicy: RestorePolicy,
|
||||
minWidth: f64,
|
||||
minHeight: f64,
|
||||
titlebar: Titlebar,
|
||||
@@ -1660,11 +1673,11 @@ const WindowsAdapterTestCore = struct {
|
||||
};
|
||||
|
||||
const descriptors = [_]Descriptor{
|
||||
.{ .label = "one", .canvasLabel = "one-canvas", .title = "", .width = 100, .height = 100, .x = null, .y = null, .resizable = true, .minWidth = 0, .minHeight = 0, .titlebar = .chromeless, .transparent = true, .alwaysOnTop = false, .clickThrough = false, .activateOnShow = true, .allowsFullscreen = true, .closePolicy = .quit, .onCloseCommand = "" },
|
||||
.{ .label = "two", .canvasLabel = "two-canvas", .title = "", .width = 100, .height = 100, .x = null, .y = null, .resizable = true, .minWidth = 0, .minHeight = 0, .titlebar = .standard, .transparent = false, .alwaysOnTop = false, .clickThrough = false, .activateOnShow = true, .allowsFullscreen = true, .closePolicy = .quit, .onCloseCommand = "" },
|
||||
.{ .label = "three", .canvasLabel = "three-canvas", .title = "", .width = 100, .height = 100, .x = null, .y = null, .resizable = true, .minWidth = 0, .minHeight = 0, .titlebar = .standard, .transparent = false, .alwaysOnTop = false, .clickThrough = false, .activateOnShow = true, .allowsFullscreen = true, .closePolicy = .quit, .onCloseCommand = "" },
|
||||
.{ .label = "four", .canvasLabel = "four-canvas", .title = "", .width = 100, .height = 100, .x = null, .y = null, .resizable = true, .minWidth = 0, .minHeight = 0, .titlebar = .standard, .transparent = false, .alwaysOnTop = false, .clickThrough = false, .activateOnShow = true, .allowsFullscreen = true, .closePolicy = .quit, .onCloseCommand = "" },
|
||||
.{ .label = "excess", .canvasLabel = "excess-canvas", .title = "", .width = 100, .height = 100, .x = null, .y = null, .resizable = true, .minWidth = 0, .minHeight = 0, .titlebar = .standard, .transparent = false, .alwaysOnTop = false, .clickThrough = false, .activateOnShow = true, .allowsFullscreen = true, .closePolicy = .quit, .onCloseCommand = "" },
|
||||
.{ .label = "one", .canvasLabel = "one-canvas", .title = "", .width = 100, .height = 100, .x = null, .y = null, .resizable = true, .restorePolicy = .center_on_primary, .minWidth = 0, .minHeight = 0, .titlebar = .chromeless, .transparent = true, .alwaysOnTop = false, .clickThrough = false, .activateOnShow = true, .allowsFullscreen = true, .closePolicy = .quit, .onCloseCommand = "" },
|
||||
.{ .label = "two", .canvasLabel = "two-canvas", .title = "", .width = 100, .height = 100, .x = null, .y = null, .resizable = true, .restorePolicy = .clamp_to_visible_screen, .minWidth = 0, .minHeight = 0, .titlebar = .standard, .transparent = false, .alwaysOnTop = false, .clickThrough = false, .activateOnShow = true, .allowsFullscreen = true, .closePolicy = .quit, .onCloseCommand = "" },
|
||||
.{ .label = "three", .canvasLabel = "three-canvas", .title = "", .width = 100, .height = 100, .x = null, .y = null, .resizable = true, .restorePolicy = .clamp_to_visible_screen, .minWidth = 0, .minHeight = 0, .titlebar = .standard, .transparent = false, .alwaysOnTop = false, .clickThrough = false, .activateOnShow = true, .allowsFullscreen = true, .closePolicy = .quit, .onCloseCommand = "" },
|
||||
.{ .label = "four", .canvasLabel = "four-canvas", .title = "", .width = 100, .height = 100, .x = null, .y = null, .resizable = true, .restorePolicy = .clamp_to_visible_screen, .minWidth = 0, .minHeight = 0, .titlebar = .standard, .transparent = false, .alwaysOnTop = false, .clickThrough = false, .activateOnShow = true, .allowsFullscreen = true, .closePolicy = .quit, .onCloseCommand = "" },
|
||||
.{ .label = "excess", .canvasLabel = "excess-canvas", .title = "", .width = 100, .height = 100, .x = null, .y = null, .resizable = true, .restorePolicy = .clamp_to_visible_screen, .minWidth = 0, .minHeight = 0, .titlebar = .standard, .transparent = false, .alwaysOnTop = false, .clickThrough = false, .activateOnShow = true, .allowsFullscreen = true, .closePolicy = .quit, .onCloseCommand = "" },
|
||||
};
|
||||
|
||||
pub const Msg = union(enum) { noop };
|
||||
@@ -1723,6 +1736,7 @@ test "TypeScript windows adapter keeps the declared prefix on overflow and proje
|
||||
try std.testing.expectEqual(Adapter.App.max_ui_windows, descriptors.len);
|
||||
try std.testing.expectEqualStrings("one", descriptors[0].label);
|
||||
try std.testing.expectEqual(@import("app_manifest").WindowTitlebarStyle.chromeless, descriptors[0].titlebar);
|
||||
try std.testing.expectEqual(@import("app_manifest").WindowRestorePolicy.center_on_primary, descriptors[0].restore_policy);
|
||||
try std.testing.expect(descriptors[0].transparent);
|
||||
try std.testing.expectEqualStrings("four", descriptors[3].label);
|
||||
}
|
||||
|
||||
@@ -370,6 +370,12 @@ pub fn UiAppWithFeatures(comptime ModelT: type, comptime MsgT: type, comptime fe
|
||||
x: ?f32 = null,
|
||||
y: ?f32 = null,
|
||||
resizable: bool = true,
|
||||
/// Placement policy passed to the host when creating this
|
||||
/// fresh, non-restored window. On macOS,
|
||||
/// `.center_on_primary` centers a descriptor without an authored
|
||||
/// origin on the primary screen; other hosts currently retain
|
||||
/// their native default placement.
|
||||
restore_policy: app_manifest.WindowRestorePolicy = .clamp_to_visible_screen,
|
||||
/// Content min-size floor the WINDOW enforces (macOS
|
||||
/// `contentMinSize`): the user's resize stops at the floor
|
||||
/// instead of the layout clamping/clipping panes below
|
||||
@@ -2880,6 +2886,7 @@ pub fn UiAppWithFeatures(comptime ModelT: type, comptime MsgT: type, comptime fe
|
||||
.x = descriptor.x,
|
||||
.y = descriptor.y,
|
||||
.resizable = descriptor.resizable,
|
||||
.restore_policy = descriptor.restore_policy,
|
||||
.titlebar = descriptor.titlebar,
|
||||
.transparent = descriptor.transparent,
|
||||
.always_on_top = descriptor.always_on_top,
|
||||
|
||||
@@ -21,6 +21,7 @@ const settings_window_label = "settings";
|
||||
const PanelModel = struct {
|
||||
settings_open: bool = false,
|
||||
position_settings: bool = true,
|
||||
center_settings: bool = false,
|
||||
transparent_settings: bool = false,
|
||||
bumps: u32 = 0,
|
||||
user_closes: u32 = 0,
|
||||
@@ -63,6 +64,7 @@ fn panelWindows(model: *const PanelModel, scratch: *PanelApp.WindowsScratch) []c
|
||||
.height = 240,
|
||||
.x = if (model.position_settings) 84 else null,
|
||||
.y = if (model.position_settings) 126 else null,
|
||||
.restore_policy = if (model.center_settings) .center_on_primary else .clamp_to_visible_screen,
|
||||
.min_width = 280,
|
||||
.min_height = 200,
|
||||
.titlebar = if (model.transparent_settings) .chromeless else .standard,
|
||||
@@ -245,6 +247,27 @@ test "a Msg declares the settings window, its canvas installs, and automation dr
|
||||
}
|
||||
}
|
||||
|
||||
test "a windows_fn restore policy reaches fresh WindowOptions" {
|
||||
const fixture = try Fixture.create();
|
||||
defer fixture.destroy();
|
||||
|
||||
fixture.app_state.model.position_settings = false;
|
||||
fixture.app_state.model.center_settings = true;
|
||||
try fixture.clickSettingsButton();
|
||||
const info = fixture.settingsWindowInfo() orelse return error.TestUnexpectedResult;
|
||||
|
||||
for (fixture.harness.null_platform.windows[0..fixture.harness.null_platform.window_count], 0..) |window, index| {
|
||||
if (window.id != info.id) continue;
|
||||
// Model-declared windows deliberately set restore_state=false. With
|
||||
// no authored origin this remains a fresh/default placement while
|
||||
// preserving the descriptor's policy at WindowOptions.
|
||||
try std.testing.expectEqual(support.platform.WindowInitialPlacement.default, fixture.harness.null_platform.window_placement[index]);
|
||||
try std.testing.expectEqual(support.platform.WindowRestorePolicy.center_on_primary, fixture.harness.null_platform.window_restore_policy[index]);
|
||||
return;
|
||||
}
|
||||
return error.WindowNotFound;
|
||||
}
|
||||
|
||||
test "a transparent descriptor selects premultiplied canvas alpha and an alpha-zero clear" {
|
||||
const fixture = try Fixture.create();
|
||||
defer fixture.destroy();
|
||||
|
||||
Reference in New Issue
Block a user