# Barra de pestañas

> Pestañas de editor con iconos, puntos de cambios y botón de cerrar. Componente de Pyzed para GUIs de escritorio en Python, con ejemplos y API.

Source: https://albakore.github.io/pyzed/es/library/tab-bar/

:::tip[En pocas palabras]
Las pestañas de arriba de un editor, una por documento abierto.
:::

Una fila de pestañas de editor con controles opcionales al principio y al final. `ui.Tab.for_file(path)` crea una pestaña con el nombre del archivo, su icono y la ruta como id. `dirty=True` muestra un punto que se convierte en el botón de cerrar al pasar el ratón, `preview=True` pone el nombre en cursiva y `status` lo tiñe con un color de git. `on_select(id)` y `on_close(id)` reciben el id de la pestaña.

`ui.tab_bar` · Categoría: [Navegación](https://albakore.github.io/pyzed/es/library/#navegación) · [Referencia completa →](https://albakore.github.io/pyzed/es/reference/ui/#tab_bar)

**Código:**

```python
ui.tab_bar(
    [ui.Tab.for_file("src/main.rs"),
     ui.Tab.for_file("src/app.rs", dirty=True, status="modified"),
     ui.Tab.for_file("README.md", preview=True)],
    "src/main.rs", on_select=on_select, on_close=on_close,
    start=[ui.icon_button("arrow-left", on_back, size="sm"),
           ui.icon_button("arrow-right", on_forward, size="sm")],
    end=[ui.icon_button("plus", on_new, size="sm"), ui.icon_button("columns-2", on_split, size="sm")],
)
```

## Cuándo usarlo

- Para los documentos abiertos de un editor, o cualquier conjunto de vistas que se abren y cierran.

## Uso

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

class Editor(pz.App):
    def __init__(self):
        self.open, self.active = ["main.py", "app.py"], "main.py"

    def close(self, path):
        i = self.open.index(path)
        self.open.remove(path)
        if self.active == path:
            self.active = self.open[min(i, len(self.open) - 1)] if self.open else None

    def view(self):
        return ui.tab_bar([ui.Tab.for_file(p) for p in self.open], self.active,
                          on_select=lambda p: setattr(self, "active", p), on_close=self.close)
```

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

### Pestañas

```python
pz.container(ui.tab_bar([ui.Tab.for_file("main.rs"), ui.Tab.for_file("app.rs")], "main.rs",
                        on_close=on_close), width=400)
```

```python
pz.container(ui.tab_bar([ui.Tab.for_file("main.rs"), ui.Tab.for_file("notes.md", dirty=True)], "main.rs",
                        on_close=on_close), width=400)
```

```python
pz.container(ui.tab_bar([ui.Tab.for_file("README.md", preview=True)], "README.md", on_close=on_close), width=400)
```

```python
pz.container(ui.tab_bar([ui.Tab.for_file("app.py", status="modified"),
                         ui.Tab.for_file("new.py", status="created")], "app.py", on_close=on_close), width=400)
```

```python
pz.container(ui.tab_bar([ui.Tab("home", "Home", icon="house"), ui.Tab("logs", "Logs", icon="scroll-text")],
                        "home", on_select=on_select), width=400)
```

### Controles

```python
pz.container(ui.tab_bar([ui.Tab.for_file("main.rs")], "main.rs", on_close=on_close,
                        start=[ui.icon_button("arrow-left", on_back, size="sm")],
                        end=[ui.icon_button("plus", on_new, size="sm")]), width=400)
```

```python
pz.row([ui.tab(ui.Tab.for_file("main.rs"), active=True),
        ui.tab(ui.Tab.for_file("app.rs"), active=False, dimmed=True)])
```

### Estados y eventos

Un clic central cierra una pestaña, `on_context(id)` se ejecuta con un clic derecho y `on_new` con un doble clic en el espacio vacío.

```python
ui.tab_bar([ui.Tab("app", "app.py", icon="file", pinned=True), ui.Tab("ui", "ui.py", icon="file")],
           "ui", on_close=on_close, on_context=on_context, on_new=on_new)
```

## Eventos

Barra de pestañas tiene **7 eventos 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 | De | El handler recibe | Cuándo |
| --- | --- | --- | --- |
| `on_select` | `ui.tab_bar`, `ui.tab` | `(tab_id)` | Callable que recibe el elemento elegido. |
| `on_close` | `ui.tab_bar`, `ui.tab` | `(tab_id)` | Mensaje o callable que se envía al pulsar la X. |
| `on_drag` | `ui.tab_bar` | `(tab_id)` | Se llama con el id de la pestaña cuando empieza a arrastrarse. |
| `on_drop` | `ui.tab_bar` | `(tab_id, event)` | Se llama con el id de la pestaña y el `Event` al soltar la pestaña arrastrada: `event.window_x`/`window_y` (fuera de la ventana si se soltó fuera) y `event.screen`. |
| `on_drag_over` | `ui.tab_bar` | `(index)` | Mientras `dragging` tiene valor: se llama con el índice de inserción bajo el puntero, o `None` cuando sale de la barra. |
| `on_context` | `ui.tab_bar` | `(tab_id)` | Recibe el id de la pestaña en la que se hace clic derecho. |
| `on_new` | `ui.tab_bar` | `()` | Se envía con un doble clic en el espacio vacío tras las pestañas. |

**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 Editor(pz.App):
    def __init__(self):
        self.open, self.active = ["main.py", "app.py"], "main.py"
        self.untitled = 0

    def close(self, path):               # on_close: the id of the tab
        i = self.open.index(path)
        self.open.remove(path)
        if self.active == path:
            self.active = self.open[min(i, len(self.open) - 1)] if self.open else None

    def new(self):                       # on_new: double click after the tabs
        self.untitled += 1
        self.open.append(f"untitled-{self.untitled}")
        self.active = self.open[-1]

    def view(self):
        return ui.tab_bar([ui.Tab.for_file(p) for p in self.open], self.active,
                          on_select=lambda p: setattr(self, "active", p), on_close=self.close,
                          on_new=self.new, on_context=lambda p: print("menu for", p))
```

**Declarativo:**

```python
from dataclasses import dataclass

@dataclass
class Activate:
    path: str

@dataclass
class Close:
    path: str

class Editor(pz.App):
    open, active = ["main.py", "app.py"], "main.py"

    def update(self, message):
        match message:
            case Activate(path):
                self.active = path
            case Close(path):
                self.open = [p for p in self.open if p != path]
                self.active = self.open[0] if self.open else None

    def view(self):
        return ui.tab_bar([ui.Tab.for_file(p) for p in self.open], self.active,
                          on_select=Activate, on_close=Close)
```

**Funcional:**

```python
from dataclasses import dataclass

@dataclass(frozen=True)
class State:
    open: tuple = ("main.py", "app.py")
    active: str = "main.py"

def view(state, set_state):
    def close(path):
        rest = tuple(p for p in state.open if p != path)
        set_state(open=rest, active=state.active if state.active != path else (rest[0] if rest else ""))
    return ui.tab_bar([ui.Tab.for_file(p) for p in state.open], state.active,
                      on_select=lambda p: set_state(active=p), on_close=close)
```

## Propiedades y API

### `ui.tab_bar`

```python
tab_bar(
    tabs,
    active,
    *,
    on_select=None,
    on_close=None,
    start=(),
    end=(),
    height=32,
    on_drag=None,
    on_drop=None,
    on_drag_over=None,
    dragging=None,
    drop_index=None,
    on_context=None,
    on_new=None,
    theme=None,
)
```

Una fila de pestañas con controles opcionales al principio y al final (navegación, dividir, +). El botón de cerrar aparece en la activa y al pasar el ratón. Las pestañas se pueden arrastrar para reordenarlas o sacarlas de la ventana: ver [Pestañas arrastrables](https://albakore.github.io/pyzed/es/guides/windows/#pestañas-arrastrables-y-ventanas-desprendibles).

| Propiedad | Por defecto | Descripción |
| --- | --- | --- |
| `tabs` | *obligatorio* | Lista de `Tab`. |
| `active` | *obligatorio* | Id de la pestaña activa. |
| `start` | `()` | Widgets al inicio (izquierda). |
| `end` | `()` | Widgets al final (derecha). |
| `height` | `32` | Alto. |
| `dragging` | `None` | Id de la pestaña que se arrastra (la apaga y activa `on_drag_over`). |
| `drop_index` | `None` | Dibuja la marca de inserción antes de la pestaña de este índice (`len(tabs)`: después de la última). |
| `theme` | `None` | Tema a usar en lugar del actual (`pyzed.theme.current()`). |

Eventos: `on_select`, `on_close`, `on_drag`, `on_drop`, `on_drag_over`, `on_context`, `on_new` ([eventos](#eventos)).

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

### `ui.Tab`

```python
@dataclass
class Tab(id, label, icon=None, icon_color=None, dirty=False, preview=False, status=None, pinned=False, tooltip=None)
```

Una pestaña. `Tab.for_file(path, **kwargs)` la crea con el nombre y el icono del archivo.

| Propiedad | Por defecto | Descripción |
| --- | --- | --- |
| `id` | *obligatorio* | Identificador estable para enfocarlo o desplazarlo con un `Command`. |
| `label` | *obligatorio* | Texto de la etiqueta. |
| `icon` | `None` | Nombre de un icono de Lucide (`pyzed icons` los lista). |
| `icon_color` | `None` | Color del icono: un token del tema o un color hex. |
| `dirty` | `False` | Cambios sin guardar (muestra un punto). |
| `preview` | `False` | Pestaña de vista previa (en cursiva). |
| `status` | `None` | Estado de git (tiñe el nombre). |
| `pinned` | `False` | Muestra solo el icono, no tiene botón de cerrar y va primera en `tab_bar`. |
| `tooltip` | `None` | Texto al pasar el ratón (la ruta completa…). |

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

### `ui.tab`

```python
tab(item, *, active, on_select=None, on_close=None, height=32, dimmed=False, theme=None)
```

Una sola pestaña. Normalmente se usa `tab_bar`.

| Propiedad | Por defecto | Descripción |
| --- | --- | --- |
| `item` | *obligatorio* | El `Tab`. |
| `active` | *obligatorio* | Si es la activa. |
| `height` | `32` | Alto. |
| `dimmed` | `False` | Apaga la etiqueta (la pestaña que se arrastra). |
| `theme` | `None` | Tema a usar en lugar del actual (`pyzed.theme.current()`). |

Eventos: `on_select`, `on_close` ([eventos](#eventos)).

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

## Relacionados

- [Barra de herramientas](https://albakore.github.io/pyzed/es/library/toolbar/): La barra bajo las pestañas: migas de pan y acciones.
- [Migas de pan](https://albakore.github.io/pyzed/es/library/breadcrumbs/): `src › main.rs › fn main`, pulsables si quieres.
- [Segmentado](https://albakore.github.io/pyzed/es/library/segmented/): Un grupo de botones de texto excluyentes.
