# Chapter 22: Desktop UI

> ⚠ **Planned — not in Loup 1.25.** This chapter describes the design in the Language Reference (§2-4, "Reactive UI", and §2-4.8, "Desktop Backend"). The toolchain does not implement it yet, so its listings are design sketches and are not verified.

Loup's specification includes a reactive UI library that is shared between the browser and the desktop: the same signals, components and `ui` fences render as HTML in a browser and as native windows on the desktop. None of it ships in 1.25.1. There is no `ui` or `ui.desktop` module (`'ui.desktop' is not a standard-library module`), no signals, and no windowing.

Two things with similar names do exist and are easy to confuse with it:

- `loup-bootstrap build --target desktop` packages a program as a self-contained application directory (a launcher executable plus the program compiled to WebAssembly, chapter 24). It produces a console program, not a windowed one.
- The `native.window` module (Packages Reference §21.35), a bare OS window with a GPU surface for games and renderers, is also absent.

The rest of this chapter is a short tour of the planned design, so that you can recognize it and so that code written now in other modes moves over easily when it lands.

## Signals, components and a window

State lives in *signals*. `signal(v)` makes a writable signal; `get()` reads it and records the dependency, and `set(v)` or `update(f)` writes it and re-renders whatever depends on it (§2-4.1). A component is a function that returns a `View`, usually built with a `ui` fence, whose layout primitives (`Row`, `Column`, `Text`, `Button`, `TextField`, …) are the same on every backend (§2-4.6). On the desktop, `createWindow` opens a window and `mount` attaches a component to it (§2-4.8):

````loup,ignore
@mode(host.native)

from ui import (signal, mount);
from ui.desktop import (createWindow, WindowConfig);

func TodoApp(): View {
    let items = signal(list[string]());
    let input = signal("");

    return ```(ui)
      <Column spacing={$ 12 $}>
        <Row spacing={$ 8 $}>
          <TextField value={$ input $} placeholder="Add item..." />
          <Button onclick={$ func(): void {
              items.update(func(xs: list[string]): list[string] => xs + [input.get()]);
              input.set("");
          } $}>
            <Text>Add</Text>
          </Button>
        </Row>
        {% for (item in items.get()) %}
          <Text>{$ item $}</Text>
        {% end %}
      </Column>
    ```;
}

func main(): void {
    let win = createWindow(WindowConfig(title = "Todo", width = 640, height = 480));
    mount(TodoApp(), win);
}
````

*Listing 22-1: A to-do window (design sketch)*

The specified `Window` has `title`, `width`, `height`, `resizable`, `fullscreen` and `visible` properties and `close()`, `minimize()`, `maximize()`, `focus()` and `setIcon(path)` methods. Its properties behave like signals: reading `win.width` inside an `effect` re-runs the effect when the window is resized.

Under ARC, a closure that an object keeps (an event handler, an effect) and that refers back to that object forms a reference cycle. The specification breaks it with `[weak self]` capture lists (§1-7.3), which 1.25.1 does not accept yet (chapter 4); expect UI code to need them.

## Menus, tray icons and dialogs

The desktop backend adds the parts of an application that a browser does not have (§2-4.8):

```loup,ignore
@mode(host.native)

from ui.desktop import (Menu, MenuBar, MenuItem, setMenuBar, openFileDialog, FileDialogOptions, FileFilter);

func installMenus(win: Window): void {
    let file = Menu(title = "File");
    file.addItem(MenuItem(label = "Open...", shortcut = "Cmd+O", onclick = func(): void { openDocument(); }));
    file.addSeparator();
    file.addItem(MenuItem(label = "Quit", shortcut = "Cmd+Q", onclick = func(): void { exit(0); }));
    let bar = MenuBar();
    bar.addMenu(file);
    setMenuBar(win, bar);
}

async func chooseFile(): Task[Option[string]] {
    return await openFileDialog(FileDialogOptions(
        title = "Open a document",
        filters = [FileFilter(name = "Text", extensions = ["txt", "md"])],
    ));
}
```

*Listing 22-2: A menu bar and a file dialog (design sketch)*

The dialogs (`openFileDialog`, `openFilesDialog`, `saveFileDialog`, `openDirectoryDialog`, `alertDialog`, `confirmDialog`) are `async` functions, so they also depend on the native async executor of chapter 21. `createTray()` returns a `Tray` with `setIcon`, `setTooltip`, `setMenu`, `show`, `hide` and `destroy`.

## One entry point for browser and desktop

A program that uses only `ui` (and none of `ui.web` or `ui.desktop`) is meant to run unchanged in a browser and on the desktop. `runtime.ui.rootMount()` returns the right mount target for the backend: the page's root element in a browser, the primary window on the desktop (§2-4.9). The browser half of this design is the subject of *The Loup Web Book*, where it is planned as well.

## Summary

- The reactive UI library (`ui`, signals, `ui` fences) and its desktop backend (`ui.desktop`: windows, menus, tray icons, dialogs) are specified but not implemented in Loup 1.25.
- `--target desktop` packages a console program; it does not provide a GUI.
- The design shares components between browser and desktop and mounts them with `mount(view, window)`.
- Chapter 23 previews SIMD and GPU compute, which are also planned.
