---
title: "Mehrere Fenster — zenit Zig UI Doku"
description: "Erstellen Sie isolierte native Fenster für Editoren, Vorschauen oder Werkzeugpanels, alle gesteuert von einer Event-Loop, die nach window_id verteilt."
url: https://zenit.z.express/de/docs/advanced/multi-window
language: de
alternate_en: https://zenit.z.express/docs/advanced/multi-window.md
alternate_zh: https://zenit.z.express/zh/docs/advanced/multi-window.md
alternate_es: https://zenit.z.express/es/docs/advanced/multi-window.md
alternate_ja: https://zenit.z.express/ja/docs/advanced/multi-window.md
alternate_ko: https://zenit.z.express/ko/docs/advanced/multi-window.md
alternate_fr: https://zenit.z.express/fr/docs/advanced/multi-window.md
project: zenit v0.1.0-alpha (Zig 0.15.2, macOS)
source: https://github.com/version-next/zenit
---

# 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

`main.zig`

```zig
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.

> NOTE
> 
> **Höchstens 16 Fenster.** `MultiWindowApp.max_windows` ist 16 – eine explizite Grenze für das allokationsfreie Lebenszyklus-Register. Darüber hinaus liefert `createWindow*` `error.TooManyWindows`.

## Isolationsgarantien

PER-WINDOW STACK

Ein Event gelangt in genau ein Fenster, bestimmt durch window\_id; das gemeinsame Modell liegt außerhalb, und die App benachrichtigt jedes Fenster explizit.

**Pro Fenster getrennt**

Natives Fenster, Cx (mit eigenem reaktivem Graphen), Font-Kontext, Metal-Surface / Renderer / Device-Queue, IME und Accessibility-Route.

**Isolierte Events**

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.

**Unabhängiger Abbau**

Das Schließen eines Fensters baut nur dessen eigene Ressourcen ab, die anderen rendern weiter; run() kehrt zurück, wenn das letzte schließt.

[Video](https://zenit.z.express/media/devtools-console.mp4?v=05e8c163e3)

Eine echte App mit zwei Fenstern: Das beobachtete Target und DevTools haben jeweils eigenen Cx und eigenes Fenster. Der Harness schaltet das Werkzeugfenster auf Console und filtert, während das Target weiterläuft.

## Lebenszyklus

| API | Funktion |
| --- | --- |
| `run()` | Treibt die Schleife, bis das letzte Fenster schließt oder quit() aufgerufen wird |
| `tick()` | Eine Runde Pump + Frame, um die Schleife selbst zu steuern |
| `closeWindow(id)` | Schließt ein Fenster; aus einem Callback heraus wird es bis zu einer sicheren Grenze eingereiht |
| `quit()` | Beendet die ganze App; verbleibende Fenster baut deinit() geordnet ab |
| `window(id)` | Findet ein lebendes Fenster per id; null, sobald es geschlossen ist |
| `activateWindow(id)` | Macht ein Fenster zum aktiven |
| `setMenuModel` / `bindMenuCommand` | Das Menümodell ist prozessweit; Befehls-Callbacks werden pro Fenster gebunden |

`close.zig`

```zig
const 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
}
```

> WARNING
> 
> **\*App ist nach dem Schließen ungültig.** Sobald ein Fenster abgebaut ist, hängt jeder `*App`\-Zeiger, den Sie dafür hielten, ins Leere. Speichern Sie die Fenster-id und holen Sie das Fenster bei Bedarf mit `application.window(id)`.

## 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.
    

> TIP
> 
> **DevTools ist auch ein Fenster.** Das vollständige Elements- / Components- / Console- / Performance-Panel kann in einem zweiten Fenster leben und den Cx eines anderen Fensters beobachten; für einen schnellen Blick auf Layout-Grenzen ist das einzeilige Overlay leichter. Siehe [DevTools](https://zenit.z.express/de/docs/advanced/devtools).
