# Buscador

> Una lista con búsqueda en una superficie flotante, con coincidencia difusa. Componente de Pyzed para GUIs de escritorio en Python, con ejemplos y API.

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

:::tip[En pocas palabras]
Un buscador flotante con una lista de resultados que se reduce mientras escribes.
:::

Un buscador sobre una lista de resultados en una superficie flotante: la base de la paleta de comandos y del buscador de archivos. `ui.fuzzy_filter(query, items, key=)` ordena los elementos (el mejor primero) y devuelve `[(item, positions), ...]`; pasa esos pares a `picker` para resaltar las coincidencias. `ui.fuzzy_match` puntúa una cadena y `ui.highlighted` dibuja una con sus coincidencias.

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

**Código:**

```python
files = ["src/app.rs", "src/render.rs", "python/pyzed/app.py", "python/pyzed/theme.py", "README.md"]
items = ui.fuzzy_filter("app", [ui.PaletteItem(f, on_open, icon="file-code") for f in files],
                        key=lambda item: item.label)
ui.picker("app", on_query, items, selected=0, placeholder="Search files…", width=480)
```

## Cuándo usarlo

- Para tus propios buscadores: archivos, símbolos, ramas, proyectos, emoji.

## Uso

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

class Finder(pz.App):
    def __init__(self):
        self.query, self.selected = "", 0
        self.files = ["src/app.rs", "src/render.rs", "README.md"]

    def view(self):
        matches = ui.fuzzy_filter(self.query, self.files)
        items = [(ui.PaletteItem(f, lambda f=f: self.open(f), icon="file"), positions)
                 for f, positions in matches[:50]]
        return ui.picker(self.query, lambda q: setattr(self, "query", q), items,
                         selected=self.selected, placeholder="Search files…")
```

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

### Resultados

```python
files = ["src/app.rs", "src/render.rs", "app.py"]
items = ui.fuzzy_filter("app", [ui.PaletteItem(f, on_open, icon="file-code") for f in files],
                        key=lambda item: item.label)
ui.picker("app", on_query, items, width=380)
```

```python
ui.picker("zzz", on_query, [], empty_text="No files match", width=380)
```

```python
ui.picker("", on_query, [(ui.PaletteItem("main", on_open, icon="git-branch", detail="2 hours ago"), []),
                        (ui.PaletteItem("docs", on_open, icon="git-branch", detail="yesterday"), [])], width=380)
```

### Resaltado

```python
t = pz.theme.current()
score, positions = ui.fuzzy_match("tgsw", "editor: toggle soft wrap")
ui.highlighted("editor: toggle soft wrap", positions, color=t.text, match_color=t.accent, size=15)
```

## Ejemplos

### Resaltar coincidencias

`fuzzy_match` devuelve `(score, positions)` o `None`; `highlighted` pinta esas posiciones.

**Código:**

```python
t = pz.theme.current()
score, positions = ui.fuzzy_match("tgsw", "editor: toggle soft wrap")
ui.highlighted("editor: toggle soft wrap", positions, color=t.text, match_color=t.accent, size=16)
```

## Eventos

Buscador tiene **2 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 | El handler recibe | Cuándo |
| --- | --- | --- |
| `on_query` | `(text)` | Callable que recibe el texto nuevo. |
| `on_submit` | `()` | Mensaje o callable que se envía al pulsar Enter. |

**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 Finder(pz.App):
    def __init__(self):
        self.query, self.opened = "", None
        self.files = ["src/app.rs", "src/render.rs", "README.md"]

    def matches(self):
        return ui.fuzzy_filter(self.query, self.files)

    def open_first(self):                # on_submit: Enter
        found = self.matches()
        self.opened = found[0][0] if found else None

    def view(self):
        items = [(ui.PaletteItem(f, lambda f=f: setattr(self, "opened", f), icon="file"), positions)
                 for f, positions in self.matches()]
        return ui.picker(self.query, lambda q: setattr(self, "query", q), items,   # on_query
                         on_submit=self.open_first, placeholder="Search files…")
```

**Declarativo:**

```python
from dataclasses import dataclass

@dataclass
class Query:
    text: str

@dataclass
class Open:
    path: str

class Finder(pz.App):
    query, opened = "", None
    files = ["src/app.rs", "src/render.rs", "README.md"]

    def update(self, message):
        match message:
            case Query(text):
                self.query = text
            case Open(path):
                self.opened = path

    def view(self):
        items = [(ui.PaletteItem(f, Open(f), icon="file"), p) for f, p in ui.fuzzy_filter(self.query, self.files)]
        return ui.picker(self.query, Query, items, placeholder="Search files…")
```

**Funcional:**

```python
from dataclasses import dataclass

FILES = ["src/app.rs", "src/render.rs", "README.md"]

@dataclass(frozen=True)
class State:
    query: str = ""
    opened: str = ""

def view(state, set_state):
    items = [(ui.PaletteItem(f, lambda f=f: set_state(opened=f), icon="file"), p)
             for f, p in ui.fuzzy_filter(state.query, FILES)]
    return ui.picker(state.query, lambda q: set_state(query=q), items, placeholder="Search files…")
```

## Propiedades y API

### `ui.picker`

```python
picker(
    query,
    on_query,
    items,
    *,
    selected=0,
    placeholder='Search…',
    input_id='picker',
    on_submit=None,
    width=560,
    max_height=340,
    empty_text='No matches',
    theme=None,
)
```

Una lista con buscador en una superficie flotante. Es la base de la paleta y del buscador de archivos.

| Propiedad | Por defecto | Descripción |
| --- | --- | --- |
| `query` | *obligatorio* | Texto buscado. |
| `items` | *obligatorio* | Pares `(PaletteItem, posiciones)`, por ejemplo de `fuzzy_filter`. |
| `selected` | `0` | Índice resaltado. |
| `placeholder` | `"Search…"` | Texto de ayuda cuando está vacío. |
| `input_id` | `"picker"` | Id del campo. |
| `width` | `560` | Ancho: píxeles, `"fill"`, `"shrink"` o `"fill:N"` (proporción). |
| `max_height` | `340` | Alto máximo en píxeles. |
| `empty_text` | `"No matches"` | Texto cuando no hay resultados. |
| `theme` | `None` | Tema a usar en lugar del actual (`pyzed.theme.current()`). |

Eventos: `on_query`, `on_submit` ([eventos](#eventos)).

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

### `ui.fuzzy_filter`

```python
fuzzy_filter(query, items, key=<class 'str'>)
```

Filtra y ordena `items` por puntuación difusa. Devuelve `[(item, posiciones), ...]`.

| Propiedad | Por defecto | Descripción |
| --- | --- | --- |
| `query` | *obligatorio* | Texto buscado. |
| `items` | *obligatorio* | Elementos. |
| `key` | `<class 'str'>` | Función que da el texto de cada elemento. |

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

### `ui.fuzzy_match`

```python
fuzzy_match(query, candidate)
```

Devuelve `(puntuación, posiciones)` si todas las letras de `query` aparecen en orden en `candidate`; si no, `None`. Premia los inicios de palabra y las letras consecutivas.

| Propiedad | Por defecto | Descripción |
| --- | --- | --- |
| `query` | *obligatorio* | Texto buscado. |
| `candidate` | *obligatorio* | Texto candidato. |

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

### `ui.highlighted`

```python
highlighted(text, positions, *, color, match_color, size=13.5, weight=None)
```

Texto con los caracteres de `positions` resaltados (coincidencias de la búsqueda difusa).

| Propiedad | Por defecto | Descripción |
| --- | --- | --- |
| `text` | *obligatorio* | Texto. |
| `positions` | *obligatorio* | Índices a resaltar. |
| `color` | *obligatorio* | Color del texto o del icono. |
| `match_color` | *obligatorio* | Color de las coincidencias. |
| `size` | `13.5` | Tamaño. |
| `weight` | `None` | Peso del texto. |

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

## Relacionados

- [Paleta de comandos](https://albakore.github.io/pyzed/es/library/command-palette/): Una paleta de comandos con búsqueda difusa y atajos.
- [Campo de búsqueda](https://albakore.github.io/pyzed/es/library/search-input/): Un campo con lupa, para filtrar.
- [Capa](https://albakore.github.io/pyzed/es/library/layer/): Dibuja una capa encima de tu vista; al pulsar fuera se cierra.
