# Popover

> Floating content at a point: context menus, color pickers. A native Pyzed component for Python desktop GUIs, with a live preview, code examples and its API.

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

:::tip[In plain words]
A small floating box that appears next to where you clicked, like a right-click menu.
:::

Shows `content` (a menu, a card…) over `base` at `(x, y)`. Clicking anywhere else calls `on_dismiss`; with `content=None` only `base` is shown. For a right-click menu, store the pointer position with `pz.mouse_area(on_move=...)` and open it from `on_right_press`.

`ui.popover` · Category: [Overlays](https://albakore.github.io/pyzed/library/#overlays) · [Full reference →](https://albakore.github.io/pyzed/reference/ui/#popover)

**Code:**

```python
ui.popover(
    main_view(),
    ui.menu([
        ui.MenuItem("Copy", on_copy, icon="copy", keys="ctrl-c"),
        ui.MenuItem("Paste", on_paste, icon="clipboard", keys="ctrl-v"),
        ui.SEPARATOR,
        ui.MenuItem("Delete", on_delete, icon="trash-2", danger=True),
    ], width=220),
    x=180, y=70, on_dismiss=on_close,
)
```

## When to use

- For context menus, dropdown panels and pickers anchored to a point or a button.

## Usage

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

class Canvas(pz.App):
    def __init__(self):
        self.pointer, self.menu_at = (0, 0), None

    def view(self):
        base = pz.mouse_area(self.content(), on_move=lambda x, y: setattr(self, "pointer", (x, y)),
                             on_right_press=lambda: setattr(self, "menu_at", self.pointer))
        menu = ui.menu([ui.MenuItem("Copy", self.copy), ui.MenuItem("Paste", self.paste)])
        x, y = self.menu_at or (0, 0)
        return ui.popover(base, menu if self.menu_at else None, x=x, y=y,
                          on_dismiss=lambda: setattr(self, "menu_at", None))
```

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

### Content

```python
ui.popover(main_view(), ui.menu([ui.MenuItem("Copy", on_copy, icon="copy"),
                                    ui.MenuItem("Paste", on_paste, icon="clipboard")], width=180),
           x=60, y=40, on_dismiss=on_close)
```

```python
ui.popover(main_view(), ui.card(ui.muted("Any widget fits here."), title="Popover", width=220),
           x=120, y=40, on_dismiss=on_close)
```

## Events

Popover 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_dismiss` | `()` | Message or callable sent on close. |

**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 Canvas(pz.App):
    def __init__(self):
        self.pointer, self.menu_at = (0, 0), None

    def view(self):
        base = pz.mouse_area(main_view(), on_move=lambda x, y: setattr(self, "pointer", (x, y)),
                             on_right_press=lambda: setattr(self, "menu_at", self.pointer))
        menu = ui.menu([ui.MenuItem("Copy", lambda: setattr(self, "menu_at", None))])
        x, y = self.menu_at or (0, 0)
        return ui.popover(base, menu if self.menu_at else None, x=x, y=y,
                          on_dismiss=lambda: setattr(self, "menu_at", None))   # click outside
```

**Declarative:**

```python
from dataclasses import dataclass

@dataclass
class MenuAt:
    x: float
    y: float

class Canvas(pz.App):
    menu_at = None

    def update(self, message):
        match message:
            case MenuAt(x, y):
                self.menu_at = (x, y)
            case "close":
                self.menu_at = None

    def view(self):
        base = pz.canvas([], width=400, height=200, on_right_press=MenuAt)
        x, y = self.menu_at or (0, 0)
        return ui.popover(base, ui.menu([ui.MenuItem("Copy", "close")]) if self.menu_at else None,
                          x=x, y=y, on_dismiss="close")
```

**Functional:**

```python
from dataclasses import dataclass

@dataclass(frozen=True)
class State:
    menu_at: tuple = ()

def view(state, set_state):
    base = pz.canvas([], width=400, height=200, on_right_press=lambda x, y: set_state(menu_at=(x, y)))
    menu = ui.menu([ui.MenuItem("Copy", lambda: set_state(menu_at=()))]) if state.menu_at else None
    x, y = state.menu_at or (0, 0)
    return ui.popover(base, menu, x=x, y=y, on_dismiss=lambda: set_state(menu_at=()))
```

## Properties and API

### `ui.popover`

```python
popover(base, content=None, *, x=0, y=0, on_dismiss=None, align_x='start')
```

Shows `content` (a `ui.menu`, a card…) over `base` at `(x, y)`. Clicking anywhere else calls `on_dismiss`. For a right-click menu, store the mouse position with `mouse_area(on_move=...)` and open the popover from `on_right_press` (see `examples/context_menu.py`).

| Property | Default | Description |
| --- | --- | --- |
| `base` | *required* | The UI underneath. |
| `content` | `None` | What floats (or `None`). |
| `x` | `0` | Horizontal position. |
| `y` | `0` | Vertical position. |
| `align_x` | `"start"` | Horizontal alignment: `"start"`/`"left"`, `"center"`, `"end"`/`"right"`. |

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

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

## Related

- [Menu](https://albakore.github.io/pyzed/library/menu/): A context menu with icons, shortcuts and checks.
- [Layer](https://albakore.github.io/pyzed/library/layer/): Draws an overlay over your view; clicking outside dismisses it.
- [Split button](https://albakore.github.io/pyzed/library/split-button/): A main action plus an arrow that opens more options.
