Apps mit mehreren Fenstern
Erstellen Sie isolierte native Fenster für Editoren, Vorschauen oder Werkzeugpanels, alle gesteuert von einer Event-Loop, die nach window_id verteilt.
Wann einsetzen
Apps mit einem Fenster verwenden weiterhin App; dessen API bleibt unverändert. Wechseln Sie nur dann zu MultiWindowApp (auch exportiert als zenit_app.Application), wenn Fenster einen eigenen Lebenszyklus, eigenes Input-Routing, eine eigene GPU-Surface oder einen eigenen Accessibility-Baum brauchen.
Fenster erstellen
const std = @import("std");
const ui = @import("ui");
const zenit_app = @import("zenit_app");
fn mountEditor(cx: *ui.Cx, scope: *ui.Scope) anyerror!*ui.Node {
_ = scope;
return ui.box(cx, .{ .width = .fill(), .height = .fill() }, .{});
}
fn mountPreview(cx: *ui.Cx, scope: *ui.Scope) anyerror!*ui.Node {
_ = scope;
return ui.box(cx, .{ .width = .fill(), .height = .fill() }, .{});
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
var application = zenit_app.MultiWindowApp.init(gpa.allocator(), .{});
defer application.deinit();
// createWindowWith = create + mount in one transaction: if mount fails,
// every native / GPU resource of that window is torn down again.
const editor = try application.createWindowWith(.{
.window = .{ .width = 900, .height = 700, .title = "Editor" },
}, mountEditor);
_ = try application.createWindowWith(.{
.window = .{ .width = 480, .height = 700, .title = "Preview" },
}, mountPreview);
_ = application.activateWindow(editor.windowId());
try application.run(); // returns after the last window closes or quit()
}Jeder Aufruf von createWindow* liefert ein *App und nimmt dieselbe Konfiguration wie ein App mit einem Fenster (window, console, frame_pacing, …). createWindow erstellt, ohne zu mounten – Sie rufen app.mount(mountFn) selbst auf; createWindowWith erledigt beides in einer Transaktion. Das Beispiel zig build multi-window im Repository zeigt beide Varianten.
Isolationsgarantien
Natives Fenster, Cx (mit eigenem reaktivem Graphen), Font-Kontext, Metal-Surface / Renderer / Device-Queue, IME und Accessibility-Route.
Zeiger-, Tastatur-, IME- und Drag-Events werden nach nativer window_id geroutet; Menübefehle gehen an das beim Auslösen erfasste Key-Window, nicht an das Fenster, das sie zufällig aus der Queue holt.
Das Schließen eines Fensters baut nur dessen eigene Ressourcen ab, die anderen rendern weiter; run() kehrt zurück, wenn das letzte schließt.
Lebenszyklus
run()Treibt die Schleife, bis das letzte Fenster schließt oder quit() aufgerufen wirdtick()Eine Runde Pump + Frame, um die Schleife selbst zu steuerncloseWindow(id)Schließt ein Fenster; aus einem Callback heraus wird es bis zu einer sicheren Grenze eingereihtquit()Beendet die ganze App; verbleibende Fenster baut deinit() geordnet abwindow(id)Findet ein lebendes Fenster per id; null, sobald es geschlossen istactivateWindow(id)Macht ein Fenster zum aktivensetMenuModel / bindMenuCommandDas Menümodell ist prozessweit; Befehls-Callbacks werden pro Fenster gebundenconst preview_id = preview.windowId(); // preview: *App from createWindowWith
// Safe from inside an input / menu / render callback: the close is queued
// and committed at the loop's iteration boundary.
_ = application.closeWindow(preview_id);
// Look windows up by id instead of holding *App across a close.
if (application.window(preview_id)) |app| {
_ = app; // still alive
}Zustand teilen
Das fensterübergreifende Modell gehört der App; jedes Fenster hält nur seinen eigenen View-Zustand. Jeder Cx hat seinen eigenen reaktiven Graphen – lassen Sie also kein Effect eines Fensters ein Signal aus dem Scope eines anderen lesen, und teilen Sie niemals Node-Zeiger oder Scope-Ressourcen über Cx hinweg.
- ✓
Das Modell lebt außerhalb der Fenster. Halten Sie Fakten in einem einfachen Struct oder einem App-weiten Store, der jedes einzelne Fenster überlebt.
- ✓
Jedes Fenster spiegelt, was es braucht. Ein Fenster erzeugt Signals in seinem eigenen Scope; ändert sich das Modell, schreibt die App explizit in den Spiegel jedes Fensters.
- ✓
Beim Schließen abmelden. Nachdem ein Fenster geschlossen ist, darf das Modell keine Zeiger auf dessen Signals mehr halten.