# TabBar

> Barra de abas com indicador animado e conteúdo rolável horizontalmente com snap por página.

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

# TabBar

`TabBar` é uma barra de abas: uma tira de botões (tab strip) + um painel de conteúdo **rolável horizontalmente** com **snap por página** (via `PageScrollEngine`). Tocar numa aba rola o conteúdo até a página correspondente; **arrastar** o conteúdo troca a aba ativa. Um **indicador animado** acompanha a aba ativa.

A estética (indicador, abas no topo ou rodapé, cores) vem do `Skin` ativo (`TabBarStyle`). Cada aba é descrita por um `TabBar.Tab` (texto, ícone ou ambos).

> 🟡 · **Pacote:** `dev.crossfly.ui` · **Estende:** `Component` · **Estilo:** `TabBarStyle` (via `getSkin().tabBar()`)

## Quando usar
- Navegação entre 2–5 seções de uma tela (Home / Buscar / Perfil).
- Conteúdo paginado com gesto de swipe entre páginas + abas sincronizadas.

## Quando NÃO usar
- Muitas seções que não cabem (a tira rola, mas fica menos legível) → considere outra navegação.

## Aparência
Tira de abas de largura igual (`1/n`); se elas não couberem, a tira vira rolável horizontalmente e as abas passam a largura automática. O indicador desliza sob (ou sobre, se `tabsAtBottom`) a aba ativa. O painel de conteúdo mostra uma página por vez com snap.

## 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());

      // Uma view de conteúdo por aba (a contagem TEM que casar com o número de abas).
      Label resumo    = new Label("Conteúdo do Resumo");
      Label detalhes  = new Label("Conteúdo dos Detalhes");
      Label historico = new Label("Conteúdo do Histórico");

      TabBar tabs = new TabBar(
         new TabBar.Tab("Resumo"),
         new TabBar.Tab("Detalhes"),
         new TabBar.Tab("Histórico"));
      tabs.setContent(resumo, detalhes, historico);
      tabs.onTabChanged(i -> System.out.println("aba " + i));

      add(LAY_FILL, tabs);   // a TabBar absorve a altura restante
   }
}
```

> 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
TabBar tabs = new TabBar(
   new TabBar.Tab("Home",   new MaterialIcon("")),
   new TabBar.Tab("Buscar", new MaterialIcon("")),
   new TabBar.Tab("Perfil", new MaterialIcon("")));
tabs.setContent(homeView, searchView, profileView);
tabs.onTabChanged(i -> System.out.println("aba " + i));
add(LAY_FILL, tabs);
```

## Construtores
| Construtor | Descrição |
|---|---|
| `TabBar(Tab... tabs)` | Cria a barra com as abas (`activeIndex = 0`) |

### `TabBar.Tab` (descritor de aba)
| Construtor | Resultado |
|---|---|
| `Tab(String text)` | Só texto (`TextPos.NONE`) |
| `Tab(Icon icon)` | Só ícone (`TextPos.NONE`) |
| `Tab(String text, Icon icon)` | Texto + ícone (`TextPos.TEXT_AT_BOTTOM`) |
| `Tab(String text, Icon icon, TextPos textPos)` | Texto + ícone com posição customizada |

## Propriedades e personalização
| Propriedade / método | Tipo | O que faz |
|---|---|---|
| `setContent(Component...)` | `TabBar` | Conteúdo por aba (a contagem **deve** casar com as abas, senão `IllegalArgumentException`) |
| `setActiveTab(int)` | `TabBar` | Ativa uma aba programaticamente (com animação/scroll); no-op se inválido ou já ativo |
| `getActiveIndex()` | `int` | Índice da aba ativa |
| `getTabs()` | `Tab[]` | Descritores das abas |
| `getStrip()` / `getTabButtons()` / `getContentPanel()` | — | Acesso aos internos |
| `getIndicatorX()` / `getIndicatorW()` | `float` | Posição/largura atuais do indicador (para pintura no `Design`) |

## Callbacks e eventos
| Callback | Quando dispara |
|---|---|
| `onTabChanged(TabChangedListener)` | Ao trocar de aba, por toque, swipe ou `setActiveTab(...)` |

`TabChangedListener` é funcional: `void onTabChanged(int index)`.

## Estados
- **Ativa** — só uma aba por vez; o indicador anima até ela (duração `TabBarStyle.switchDuration`).
- **Swipe do usuário** — o indicador interpola continuamente entre abas vizinhas; ao cruzar para outra página, `activeIndex` troca e `onTabChanged` dispara.
- **Programático** — `setActiveTab`/toque travam a sincronização até a animação de scroll chegar ao alvo.

### Abas só de texto
```java
TabBar tabs = new TabBar(
   new TabBar.Tab("Resumo"),
   new TabBar.Tab("Detalhes"),
   new TabBar.Tab("Histórico"));
tabs.setContent(resumoView, detalhesView, historicoView);
add(LAY_FILL, tabs);
```

### Troca programática de aba
```java
TabBar tabs = new TabBar(new TabBar.Tab("A"), new TabBar.Tab("B"), new TabBar.Tab("C"));
tabs.setContent(viewA, viewB, viewC);
add(LAY_FILL, tabs);
// ... mais tarde:
tabs.setActiveTab(2);  // anima até a aba C
```

## Boas práticas
- A contagem de `setContent(...)` **tem** que bater com o número de abas.
- Para abas no rodapé (estilo iOS), use um `Skin` cujo `TabBarStyle.tabsAtBottom = true` — o layout e o indicador se ajustam sozinhos.
- Não force largura fixa nas abas: se não couberem, a tira rola por conta própria.

## Erros comuns
- **`setContent` com contagem errada** → `IllegalArgumentException`. Passe exatamente uma view por aba.
- **Esperar que `setActiveTab` dispare `onTabChanged` para o índice já ativo** — é no-op quando `index == activeIndex`.

## Veja também
- Guia da área: [`ui/`](/docs/ui) · Receitas: [`cookbook/`](/docs/cookbook)
