# Popover

> Contenido flotante en un punto: menús contextuales, selectores de color. Componente de Pyzed para GUIs de escritorio en Python, con ejemplos y API.

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

:::tip[En pocas palabras]
Una cajita flotante que aparece junto a donde pulsaste, como un menú de clic derecho.
:::

Muestra `content` (un menú, una tarjeta…) encima de `base` en `(x, y)`. Al pulsar en otro sitio se llama a `on_dismiss`; con `content=None` solo se muestra `base`. Para un menú de clic derecho, guarda la posición del puntero con `pz.mouse_area(on_move=...)` y ábrelo en `on_right_press`.

`ui.popover` · Categoría: [Capas flotantes](https://albakore.github.io/pyzed/es/library/#capas-flotantes) · [Referencia completa →](https://albakore.github.io/pyzed/es/reference/ui/#popover)

**Código:**

```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,
)
```

## Cuándo usarlo

- Para menús contextuales, paneles desplegables y selectores anclados a un punto o a un botón.

## Uso

```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))
```

## Variantes

:::note[Sobre estas capturas]
Cada captura la graba pyzed mismo, fuera de pantalla a 2x, en One Dark o One Light según el tema de este sitio, y recortada al componente para que se vea a su tamaño real. El puntero pasa por encima, pulsa o escribe en él; un componente que no reacciona al puntero recorre algunos temas. Con movimiento reducido ves el primer fotograma. Abre **Código** bajo una captura para ver el fragmento que la dibuja; los nombres libres como `on_save` son tus propios callbacks.
:::

### Contenido

```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)
```

## Eventos

Popover tiene **1 evento propios**, en la tabla de abajo. Como todo widget, también tiene los **18 eventos comunes** (`click`, `double_click`, `right_click`, `mouse_enter`, `drag`, `key_down`, `resize`…), que se conectan con `.on(...)`: mira [Eventos](https://albakore.github.io/pyzed/es/guides/events/) y [Eventos de cada componente](https://albakore.github.io/pyzed/es/library/events/).

| Evento | El handler recibe | Cuándo |
| --- | --- | --- |
| `on_dismiss` | `()` | Mensaje o callable que se envía al cerrar. |

**El handler recibe** es lo que Pyzed le pasa a tu función: `()` es nada. Cualquier handler también puede ser un mensaje (un valor que no se puede llamar), que va a `update()`.

### Manejar los eventos

Los mismos eventos escritos de tres formas. **Imperativa**: métodos de la app que cambian `self`. **Declarativa**: los handlers producen mensajes y un único `update()` decide. **Funcional**: una función pura dibuja un estado inmutable y pide cambios con `set_state(...)`. Mira [Imperativo, declarativo y funcional](https://albakore.github.io/pyzed/es/guides/code-styles/).

**Imperativo:**

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

**Declarativo:**

```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")
```

**Funcional:**

```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=()))
```

## Propiedades y API

### `ui.popover`

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

Muestra `content` (un `ui.menu`, una tarjeta…) sobre `base` en `(x, y)`. Hacer clic en cualquier otro sitio llama a `on_dismiss`. Para un menú de clic derecho, guarda la posición del ratón con `mouse_area(on_move=...)` y abre el popover desde `on_right_press` (ver `examples/context_menu.py`).

| Propiedad | Por defecto | Descripción |
| --- | --- | --- |
| `base` | *obligatorio* | La interfaz de debajo. |
| `content` | `None` | Lo que flota (o `None`). |
| `x` | `0` | Posición horizontal. |
| `y` | `0` | Posición vertical. |
| `align_x` | `"start"` | Alineación horizontal: `"start"`/`"left"`, `"center"`, `"end"`/`"right"`. |

Eventos: `on_dismiss` ([eventos](#eventos)).

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

## Relacionados

- [Menú](https://albakore.github.io/pyzed/es/library/menu/): Un menú contextual con iconos, atajos y marcas.
- [Capa](https://albakore.github.io/pyzed/es/library/layer/): Dibuja una capa encima de tu vista; al pulsar fuera se cierra.
- [Botón dividido](https://albakore.github.io/pyzed/es/library/split-button/): Una acción principal más una flecha que abre más opciones.
