# Menu

> A context menu with icons, shortcuts and checks. A native Pyzed component for Python desktop GUIs, with a live preview, code examples and its API.

Source: https://albakore.github.io/pyzed/library/menu/

:::tip[In plain words]
The list of options that appears when you right-click or press a “more” button.
:::

A floating context menu. Items are `ui.MenuItem(label, on_press, icon=, keys=, checked=, danger=, disabled=)`; `ui.SEPARATOR` draws a line between groups and a plain string is a header. Show it with a [Popover](https://albakore.github.io/pyzed/library/popover/) or a [Layer](https://albakore.github.io/pyzed/library/layer/).

`ui.menu` · Category: [Lists and menus](https://albakore.github.io/pyzed/library/#lists-and-menus) · [Full reference →](https://albakore.github.io/pyzed/reference/ui/#menu)

**Code:**

```python
ui.menu([
    ui.MenuItem("New file", on_new, icon="file-plus", keys="ctrl-n"),
    ui.MenuItem("New folder", on_new_folder, icon="folder-plus"),
    ui.SEPARATOR,
    "EDIT",
    ui.MenuItem("Copy path", on_copy, keys="ctrl-alt-c"),
    ui.MenuItem("Soft wrap", on_wrap, checked=True),
    ui.SEPARATOR,
    ui.MenuItem("Delete", on_delete, icon="trash-2", danger=True),
])
```

## When to use

- For right-click menus and the options of a split button or an ellipsis button.
- Mark destructive actions with `danger=True` and put them last.

## Usage

```python
from pyzed import ui

ui.menu([
    ui.MenuItem("New file", on_new, icon="file-plus", keys="ctrl-n"),
    ui.MenuItem("New folder", on_new_folder, icon="folder-plus"),
    ui.SEPARATOR,
    "EDIT",
    ui.MenuItem("Copy path", on_copy, keys="ctrl-alt-c"),
    ui.MenuItem("Soft wrap", on_wrap, checked=True),
    ui.SEPARATOR,
    ui.MenuItem("Delete", on_delete, icon="trash-2", danger=True),
])
```

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

### Items

```python
ui.menu([ui.MenuItem("Open", on_open), ui.MenuItem("Rename", on_rename),
         ui.MenuItem("Duplicate", on_duplicate)], width=200)
```

```python
ui.menu([ui.MenuItem("Copy", on_copy, icon="copy", keys="ctrl-c"),
         ui.MenuItem("Paste", on_paste, icon="clipboard", keys="ctrl-v")], width=220)
```

```python
ui.menu([ui.MenuItem("Soft wrap", on_wrap, checked=True),
         ui.MenuItem("Minimap", on_minimap, checked=False)], width=200)
```

```python
ui.menu([ui.MenuItem("Rename", on_rename), ui.SEPARATOR,
         ui.MenuItem("Delete", on_delete, icon="trash-2", danger=True)], width=200)
```

```python
ui.menu([ui.MenuItem("Undo", on_undo, keys="ctrl-z"),
         ui.MenuItem("Redo", on_redo, keys="ctrl-shift-z", disabled=True)], width=220)
```

### Groups

```python
ui.menu(["FILE", ui.MenuItem("New", on_new, keys="ctrl-n"), ui.SEPARATOR,
         "EDIT", ui.MenuItem("Find", on_find, keys="ctrl-f")], width=220)
```

### States and events

```python
ui.menu([ui.MenuItem("Share", on_press, icon="share", submenu=True),
         ui.MenuItem("Rename", on_press, description="F2 also works")])
```

```python
ui.menu([ui.MenuItem(f"Recent {i}", on_press) for i in range(10)], max_height=140)
```

## Events

Menu has **1 event 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 | Handler gets | When |
| --- | --- | --- |
| `on_press` | `()` | Message or callable sent on press. `None` disables the control. |

**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 Files(pz.App):
    def __init__(self):
        self.wrap = True
        self.log = []

    def view(self):
        return ui.menu([
            ui.MenuItem("New file", lambda: self.log.append("new"), icon="file-plus", keys="ctrl-n"),
            ui.MenuItem("Soft wrap", lambda: setattr(self, "wrap", not self.wrap), checked=self.wrap),
            ui.SEPARATOR,
            ui.MenuItem("Delete", lambda: self.log.append("delete"), icon="trash-2", danger=True),
        ])
```

**Declarative:**

```python
class Files(pz.App):
    wrap = True

    def update(self, message):           # each item's on_press is a message
        match message:
            case "new":
                print("new file")
            case "wrap":
                self.wrap = not self.wrap

    def view(self):
        return ui.menu([ui.MenuItem("New file", "new", icon="file-plus", keys="ctrl-n"),
                        ui.MenuItem("Soft wrap", "wrap", checked=self.wrap)])
```

**Functional:**

```python
from dataclasses import dataclass

@dataclass(frozen=True)
class State:
    wrap: bool = True

def view(state, set_state):
    return ui.menu([
        ui.MenuItem("Soft wrap", lambda: set_state(wrap=not state.wrap), checked=state.wrap),
        ui.MenuItem("Delete", None, icon="trash-2", danger=True, disabled=True),
    ])
```

## Properties and API

### `ui.menu`

```python
menu(items, *, width=240, max_height=None, theme=None)
```

A floating context menu. Use `ui.SEPARATOR` between groups; a plain string is a header.

| Property | Default | Description |
| --- | --- | --- |
| `items` | *required* | List of `MenuItem`, `ui.SEPARATOR` or strings. |
| `width` | `240` | Width: pixels, `"fill"`, `"shrink"` or `"fill:N"` (proportion). |
| `max_height` | `None` | Scrolls a menu taller than this. |
| `theme` | `None` | Theme to use instead of the current one (`pyzed.theme.current()`). |

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

### `ui.MenuItem`

```python
@dataclass
class MenuItem(label, on_press=None, icon=None, keys=None, disabled=False, danger=False, checked=None, description=None, submenu=False)
```

An entry of `ui.menu`.

| Property | Default | Description |
| --- | --- | --- |
| `label` | *required* | Label text. |
| `icon` | `None` | Name of a Lucide icon (`pyzed icons` lists them). |
| `keys` | `None` | Shortcut shown at the end, written as a string: `"ctrl-s"`, `"cmd-k cmd-s"`. |
| `disabled` | `False` | Disables it (ignores clicks). |
| `danger` | `False` | In red (destructive actions). |
| `checked` | `None` | Shows a check mark (`True`/`False`) or nothing (`None`). |
| `description` | `None` | A muted second line. |
| `submenu` | `False` | Ends the row with a chevron; open the next menu beside it in `on_press`. |

Events: `on_press` ([events](#events)).

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

### `ui.SEPARATOR`

Separator for `ui.menu`.

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

## Related

- [Popover](https://albakore.github.io/pyzed/library/popover/): Floating content at a point: context menus, color pickers.
- [Split button](https://albakore.github.io/pyzed/library/split-button/): A main action plus an arrow that opens more options.
- [List item](https://albakore.github.io/pyzed/library/list-item/): A list row with icon, text and an end slot.
