# ListView

> Lista rolável e embutível de itens de texto, com seleção única ou múltipla.

Source: https://crossfly.dev/pt/docs/ui/widgets/listview — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# ListView

`ListView` é uma lista rolável de itens de **texto** para seleção, pronta para embutir em qualquer container. Cada item vira um "card" clicável. O modo de seleção é fixado no construtor via `ItemType`: `SINGLE` (toque seleciona e confirma) ou `MULTIPLE` (checkboxes + botão "Confirmar").

Ao contrário de `VirtualListView`, o `ListView` **cria um componente por item** (sem reciclagem) — é ideal para listas pequenas/médias (dezenas de itens), não para milhares de linhas.

> 🟡 · **Pacote:** `dev.crossfly.ui` · **Estende:** `Component` · **Estilo:** `ListViewStyle` (itens/header/botão) + `WindowStyle` (fundo, via `selectionWindowStyle()`)

## Quando usar
- Um seletor embutido numa tela (menu de opções, filtros, escolha de categoria).
- Poucas dezenas de itens de texto onde altura exata sem rolagem é útil (`computeIdealHeight`).
- Multi-seleção com confirmação explícita (`MULTIPLE`).

## Quando NÃO usar
- Listas grandes (centenas/milhares) → use [`VirtualListView`](/docs/ui/widgets/virtuallistview), que recicla componentes.
- Popup modal de escolha → use [`ListViewWindow`](/docs/ui/widgets/listviewwindow), que envolve o `ListView` numa `Window`.
- Linhas ricas com imagem/vários campos → use `itemFactory(...)` ou monte a lista à mão com [itens de lista](/docs/ui/widgets/list-items).

## Aparência
Coluna vertical de cards de texto centralizados, com cabeçalho opcional (título + ícone de voltar) e, em `MULTIPLE`, um botão "Confirmar" no rodapé. Em `SINGLE` cada card é um `LabelItem`; em `MULTIPLE`, um `CheckLabelItem` (checkbox + label).

## Exemplo completo (copiar, colar e rodar)
```java
package com.exemplo;

import dev.crossfly.ui.*;
import dev.crossfly.ui.layout.*;

/** Tela principal executável. Aponte o launcher (classname) e rode: gradlew :lwjgl3:run */
public class MainWindow extends Window
{
   @Override
   public void onShow() throws Exception
   {
      super.onShow();
      setLayout(new VerticalLayout());

      ListView list = new ListView(new String[]{"Um", "Dois", "Três"}, ListView.ItemType.SINGLE)
         .showHeader(false);
      list.onConfirm(e -> System.out.println("Escolhido: " + list.getSelectedText()));

      add(LAY_FILL, list);   // listas usam LAY_FILL (absorvem a altura)
   }
}
```

> Os trechos abaixo mostram **só o miolo** (o que vai dentro de `onShow()`). Para rodar, coloque-os no esqueleto `MainWindow` acima.

## Uso básico
```java
// SINGLE, sem cabeçalho, altura automática (sem rolagem)
ListView list = new ListView(new String[]{"Um", "Dois", "Três"}, ListView.ItemType.SINGLE)
   .showHeader(false);
list.onConfirm(e -> System.out.println("Escolhido: " + list.getSelectedText()));
contents.add(.95f, list);
```

## Construtores
| Construtor | Descrição |
|---|---|
| `ListView(String[] items, ItemType itemType)` | Lista sem título |
| `ListView(String title, String[] items, ItemType itemType)` | Lista com título no cabeçalho |

`ItemType` (definido em `UIConstants`): `SINGLE` ou `MULTIPLE`. O modo é **imutável** após a construção.

## Propriedades e personalização
| Propriedade / método | Tipo | Padrão | O que faz |
|---|---|---|---|
| `addItem(String)` | `ListView` | — | Adiciona um item (rebuild preguiçoso; ignora `null`) |
| `addItems(String...)` | `ListView` | — | Adiciona vários itens (uma só invalidação) |
| `showHeader(boolean)` | `ListView` | `true` | Mostra/esconde o cabeçalho (título + voltar) |
| `title(String)` | `ListView` | `null` | Define o título |
| `confirmText(String)` | `ListView` | `"Confirmar"` | Texto do botão (só `MULTIPLE`) |
| `itemStyle(ComponentStyle)` | `ListView` | tema | Override de estilo de todos os itens |
| `confirmButtonStyle(ComponentStyle)` | `ListView` | tema | Estilo do botão de confirmar |
| `headerStyle(LabelStyle)` | `ListView` | tema | Estilo do cabeçalho |
| `itemFactory(ItemFactory)` | `ListView` | `null` | Fábrica de item customizado (recebe `text`, `index`) |
| `closeAction(Runnable)` | `ListView` | `null` | Ação de fechamento (a `ListViewWindow` aponta para `this::hide`) |
| `computeIdealHeight(int width)` | `int` | — | Altura exata sem rolagem, ou `-1` se houver `itemFactory` |

### Consulta de seleção
`getSelectedCount()`, `getSelectedIndex()`, `getSelectedText()`, `getSelectedIndices()`, `getSelectedTexts()`, `isSelected(int)`, `getSelectionState()` (`boolean[]`).

## Callbacks e eventos
| Callback | Quando dispara |
|---|---|
| `onConfirm(Component.Action)` | `SINGLE`: ao tocar um item. `MULTIPLE`: ao tocar "Confirmar" |

Em `SINGLE`, o toque limpa a seleção anterior, marca o índice, dispara `onConfirm` e chama `close()`. Em `MULTIPLE`, o toque alterna o checkbox do item; a confirmação sai do botão.

## Estados
- **SINGLE** — apenas um item marcado por vez; o toque já confirma e fecha.
- **MULTIPLE** — vários itens marcáveis; estado consultável via `getSelectionState()` mesmo antes de confirmar.
- Cabeçalho e botão de rodapé são opcionais (dependem de `showHeader`/`title` e do `ItemType`).

### MULTIPLE com altura automática
```java
ListView multi = new ListView(new String[]{"Vermelho", "Verde", "Azul"},
      ListView.ItemType.MULTIPLE)
   .showHeader(false)
   .confirmText("Aplicar");
multi.getStyle().autoHeight(Relative.ofFont(9f));
multi.onConfirm(e -> {
   for (String t : multi.getSelectedTexts())
      System.out.println("marcado: " + t);
});
contents.add(.95f, multi);
```

### Linhas customizadas com `itemFactory`
```java
ListView list = new ListView(labels, ListView.ItemType.SINGLE).showHeader(false);
list.itemFactory((text, index) -> {
   Component row = new Component();
   row.style(Styles.component().pressable().ingap(Gap.ofFont(.3f)));
   row.setLayout(new GridLayout(Position.START, Position.CENTER));
   row.add(new Label(text));
   return row;
});
list.onConfirm(e -> System.out.println("idx = " + list.getSelectedIndex()));
contents.add(.95f, list);
```

## Boas práticas
- Prefira `addItems(...)` a vários `addItem(...)` — uma única invalidação de layout.
- Use `computeIdealHeight(width)` quando quiser uma lista **sem rolagem** com altura exata (retorna `-1` se houver `itemFactory`, pois itens customizados não são mensuráveis genericamente).
- A reconstrução dos itens é preguiçosa (acontece no próximo layout), inclusive quando a largura muda.

## Erros comuns
- **Usar `ListView` para milhares de itens** — ele não recicla; a memória cresce linearmente. Migre para [`VirtualListView`](/docs/ui/widgets/virtuallistview).
- **Esperar `onConfirm` em `MULTIPLE` a cada toque** — em `MULTIPLE` o callback só dispara no botão "Confirmar"; consulte `getSelectionState()` para reagir a cada marcação.
- **Assumir que existe construtor sem itens** — o construtor exige `items` e `ItemType`; comece com `new String[]{}` e use `addItems(...)`.

## Veja também
- Guia da área: [`ui/`](/docs/ui) · Receitas: [`cookbook/`](/docs/cookbook)
- Componentes relacionados: [`ListViewWindow`](/docs/ui/widgets/listviewwindow) · [`VirtualListView`](/docs/ui/widgets/virtuallistview) · [itens de lista (`LabelItem`/`CheckLabelItem`)](/docs/ui/widgets/list-items)
