# Window frame

> Your own title bar, window buttons and menus. A native Pyzed component for Python desktop GUIs, with a live preview, code examples and its API.

Source: https://albakore.github.io/pyzed/library/window-frame/

:::tip[In plain words]
Draws the window's own top bar, with its menus and minimize / maximize / close buttons.
:::

Draws the whole frame of a window opened without system decorations (`frame = "custom"`): a title bar that moves the window (double click maximizes), the minimize, maximize and close buttons in the style of a platform, menus on the left (`ui.Menu`) and borders that resize the window. `ui.window_title_bar` is the bar alone, `ui.window_controls` the three buttons and `ui.system_platform()` says which style this system uses.

`ui.window_frame` · Category: [Workspace](https://albakore.github.io/pyzed/library/#workspace) · [Full reference →](https://albakore.github.io/pyzed/reference/ui/#window_frame)

**Code:**

```python
ui.window_frame(
    ui.editor("frame-main", "import pyzed as pz\n\npz.run(App)\n", path="main.py"),
    title="notes.py",
    menus=[ui.Menu("File", [ui.MenuItem("Open…", on_open, keys="ctrl-o")]),
           ui.Menu("Edit", [ui.MenuItem("Undo", on_undo, keys="ctrl-z")])],
    start=[ui.title_button("pyzed", on_projects, chevron=True)],
    style="windows",
)
```

:::note[New]
This component was added recently. Tell us how it works for you in the [issue tracker](https://github.com/albakore/pyzed/issues).
:::

## When to use

- For apps that want a modern editor-like window: menus and pickers in the title bar instead of the system's frame. See [Window frames](https://albakore.github.io/pyzed/guides/windows/#window-frames-your-own-title-bar).
- With no callbacks, `ui.window_title_bar` and `ui.window_controls` only show: use them to preview a window in the frame of each platform.

## Usage

```python
import pyzed as pz
from pyzed import ui

class Notes(pz.App):
    title = "Notes"
    frame = "custom"  # or "macos", "windows", "gnome", "ubuntu", "kde"

    def view(self):
        return ui.window_frame(
            self.editor(),
            menus=[ui.Menu("File", [ui.MenuItem("Open…", self.open, keys="ctrl-o"), ui.SEPARATOR,
                                    ui.MenuItem("Quit", self.close)])],
            start=[ui.title_button("pyzed", chevron=True)],
            end=[ui.button("Run", self.run, icon="play", size="sm")],
        )
```

## Variants

:::note[About these captures]
Each capture is recorded by pyzed itself, off-screen at 2x, in One Dark or One Light to match this site's theme, and cropped to the component so it shows at its real size. The pointer hovers, presses or types into it; a component that does not react to the pointer cycles through a few themes. With reduced motion you see the first frame. Open **Code** under a capture to see the snippet that draws it; free names such as `on_save` stand for your own callbacks.
:::

### Platform style

```python
pz.container(ui.window_title_bar("notes.py", style="windows"), width=420)
```

```python
pz.container(ui.window_title_bar("notes.py", style="macos"), width=420)
```

```python
pz.container(ui.window_title_bar("notes.py", style="gnome"), width=420)
```

```python
pz.container(ui.window_title_bar("notes.py", style="kde"), width=420)
```

```python
pz.container(ui.window_title_bar("notes.py", style="ubuntu"), width=420)
```

### State

```python
pz.container(ui.window_title_bar("notes.py", style="windows", focused=False), width=420)
```

```python
ui.window_controls("windows", maximized=True)
```

```python
ui.window_controls("macos")
```

## Events

Window frame has **5 events of its own**, listed below. Like every widget it also has the **18 common events** (`click`, `double_click`, `right_click`, `mouse_enter`, `drag`, `key_down`, `resize`…), attached with `.on(...)`: see [Events](https://albakore.github.io/pyzed/guides/events/) and [Events of every component](https://albakore.github.io/pyzed/library/events/).

| Event | Of | Handler gets | When |
| --- | --- | --- | --- |
| `on_drag` | `ui.window_title_bar` | `()` | Called when the empty part of the bar is dragged (return `Command.drag_window()`). |
| `on_minimize` | `ui.window_title_bar`, `ui.window_controls` | `()` | Minimize button. |
| `on_maximize` | `ui.window_title_bar`, `ui.window_controls` | `()` | Maximize button and double click on the bar. |
| `on_close` | `ui.window_title_bar`, `ui.window_controls` | `()` | Close button. |
| `on_menu` | `ui.window_title_bar` | `()` | Right click on the bar. |

**Handler gets** is what Pyzed passes to your callable: `()` means nothing. Any handler can also be a message (a value that is not callable), which goes to `update()`.

### Handling the events

The same events written three ways. **Imperative**: methods of the app change `self`. **Declarative**: the handlers produce messages and one `update()` decides. **Functional**: a pure function draws an immutable state and asks for changes with `set_state(...)`. See [Imperative, declarative and functional](https://albakore.github.io/pyzed/guides/code-styles/).

**Imperative:**

```python
class Notes(pz.App):
    frame = "custom"                     # Pyzed draws the title bar

    def view(self):
        bar = ui.window_title_bar(
            "notes.py",
            on_drag=lambda: self.drag(),                 # the empty part of the bar
            on_minimize=lambda: self.minimize(),
            on_maximize=lambda: self.toggle_maximize(),  # also a double click on the bar
            on_close=lambda: self.close(),
            on_menu=lambda: print("window menu"),        # right click on the bar
            maximized=self.window_maximized,
        )
        return pz.column([bar, main_view()])
```

**Declarative:**

```python
class Notes(pz.App):
    frame = "custom"

    def update(self, message):           # the commands are returned from update
        match message:
            case "drag":
                return self.drag()
            case "minimize":
                return self.minimize()
            case "maximize":
                return self.toggle_maximize()
            case "close":
                return self.close()

    def view(self):
        return pz.column([ui.window_title_bar("notes.py", on_drag="drag", on_minimize="minimize",
                                              on_maximize="maximize", on_close="close"), main_view()])
```

**Functional:**

```python
from dataclasses import dataclass

@dataclass(frozen=True)
class State:
    title: str = "notes.py"

def view(state, set_state):
    # A handler can return a command: Pyzed runs it.
    return pz.column([ui.window_title_bar(state.title, on_drag=lambda: pz.Command.drag_window(),
                                          on_close=lambda: pz.Command.exit()), main_view()])
```

## Tips

:::tip
With only menus, define `frame_menus()` on the window and skip `ui.window_frame`: the runtime frames the view for you. `style` defaults to the window's `frame`, or this system's style.
:::

## Properties and API

### `ui.window_frame`

```python
window_frame(
    content,
    *,
    title=None,
    menus=None,
    menu_button=True,
    start=(),
    center=None,
    end=(),
    style=None,
    window=None,
    height=None,
    resize_border=6.0,
    resize_edges=True,
    bg=None,
    theme=None,
)
```

The whole frame of a window opened with `frame="custom"`: a title bar that moves the window (double click maximizes, right click opens the system menu on Windows), the minimize, maximize and close buttons in the style of a platform, menus on the left, and borders that resize the window.

| Property | Default | Description |
| --- | --- | --- |
| `content` | *required* | The window's content, under the title bar. |
| `title` | `None` | Title shown in the bar. By default the window's `title`. |
| `menus` | `None` | Menus on the left of the bar: a list of `ui.Menu`. |
| `menu_button` | `True` | Shows the menus behind a ☰ button (`False` always shows their names). |
| `start` | `()` | Items after the menus (project, branch…). |
| `center` | `None` | Your own widget in the middle instead of the title (a search field, Run and Stop…); the bar still drags on both sides of it. |
| `end` | `()` | Items before the window buttons. |
| `style` | `None` | `"macos"`, `"windows"`, `"gnome"`, `"ubuntu"` or `"kde"`. By default the window's `frame`, or this system's. |
| `window` | `None` | The window the buttons act on. By default the one whose `view()` is running. |
| `height` | `None` | Height of the bar. By default the platform's. |
| `resize_border` | `6.0` | Width in pixels of the borders that resize the window. |
| `resize_edges` | `True` | `False` leaves the resize borders to you (`pz.window_edges`), to put them over menus and dialogs. |
| `bg` | `None` | Background color (`"#rrggbb[aa]"`, `(r, g, b[, a])` or `"transparent"`). |
| `theme` | `None` | Theme to use instead of the current one (`pyzed.theme.current()`). |

[Full reference →](https://albakore.github.io/pyzed/reference/ui/#window_frame)

### `ui.Menu`

```python
@dataclass
class Menu(label, items=[])
```

A menu of a window frame's menu bar: a name and its items (`ui.MenuItem`, `ui.SEPARATOR` or a header string).

| Property | Default | Description |
| --- | --- | --- |
| `label` | *required* | The menu's name. |
| `items` | `[]` | Its entries: `ui.MenuItem`, `ui.SEPARATOR` or a header string. |

[Full reference →](https://albakore.github.io/pyzed/reference/ui/#menu-1)

### `ui.window_title_bar`

```python
window_title_bar(
    title=None,
    *,
    style=None,
    start=(),
    center=None,
    end=(),
    on_drag=None,
    on_minimize=None,
    on_maximize=None,
    on_close=None,
    on_menu=None,
    maximized=False,
    focused=True,
    width='fill',
    height=None,
    bg=None,
    border=True,
    theme=None,
)
```

A window's title bar alone: window buttons in the style of `style` (on the left for macOS), `start` items, the title and `end` items. Without callbacks it only shows, which is how Pyzed Design previews a form in the frame of each platform.

| Property | Default | Description |
| --- | --- | --- |
| `title` | `None` | Title (centered, or on the left on Windows). |
| `style` | `None` | `"macos"`, `"windows"`, `"gnome"`, `"ubuntu"` or `"kde"` (`None`: this system's). |
| `start` | `()` | Items at the start. |
| `center` | `None` | Widget in the middle instead of the title; the bar still drags on both sides of it. |
| `end` | `()` | Items at the end. |
| `maximized` | `False` | Shows the restore symbol. |
| `focused` | `True` | `False` dims it like a window in the background. |
| `width` | `"fill"` | Width: pixels, `"fill"`, `"shrink"` or `"fill:N"` (proportion). |
| `height` | `None` | Height. By default the platform's. |
| `bg` | `None` | Background color (`"#rrggbb[aa]"`, `(r, g, b[, a])` or `"transparent"`). |
| `border` | `True` | Line under the bar. |
| `theme` | `None` | Theme to use instead of the current one (`pyzed.theme.current()`). |

Events: `on_drag`, `on_minimize`, `on_maximize`, `on_close`, `on_menu` ([events](#events)).

[Full reference →](https://albakore.github.io/pyzed/reference/ui/#window_title_bar)

### `ui.window_controls`

```python
window_controls(
    style=None,
    *,
    on_minimize=None,
    on_maximize=None,
    on_close=None,
    maximized=False,
    focused=True,
    height=None,
    theme=None,
)
```

The minimize, maximize and close buttons in the style of a platform. A button without a callback is drawn but does nothing.

| Property | Default | Description |
| --- | --- | --- |
| `style` | `None` | `"macos"`, `"windows"`, `"gnome"`, `"ubuntu"` or `"kde"` (`None`: this system's). |
| `maximized` | `False` | Shows the restore symbol. |
| `focused` | `True` | `False` dims them. |
| `height` | `None` | Height. |
| `theme` | `None` | Theme to use instead of the current one (`pyzed.theme.current()`). |

Events: `on_minimize`, `on_maximize`, `on_close` ([events](#events)).

[Full reference →](https://albakore.github.io/pyzed/reference/ui/#window_controls)

### `ui.system_platform`

```python
system_platform()
```

The style of this system's window buttons: `"macos"`, `"windows"`, `"kde"`, `"ubuntu"` or `"gnome"` (other Linux desktops). Under WSL it is `"windows"`.

[Full reference →](https://albakore.github.io/pyzed/reference/ui/#system_platform)

## Related

- [Title bar](https://albakore.github.io/pyzed/library/title-bar/): The top bar with project and branch pickers.
- [Workspace](https://albakore.github.io/pyzed/library/workspace/): The layout of an editor-style window: title bar, docks, center, status bar.
- [Menu](https://albakore.github.io/pyzed/library/menu/): A context menu with icons, shortcuts and checks.
