# Como tornar um Component rolável e ajustar o overscroll

> Habilitar scroll em qualquer Component com setScrollable e configurar o efeito elástico de borda.

Source: https://crossfly.dev/pt/docs/cookbook/lists/rolagem-e-overscroll — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# Como tornar um Component rolável e ajustar o overscroll

**Propósito.** Qualquer `Component` pode virar um contêiner rolável: basta declarar a direção com
`setScrollable(ScrollDirection)`. Se o conteúdo (a soma das alturas dos filhos) exceder a área
visível, o `ScrollEngine` cuida de pan, momentum, fling e do **overscroll** (efeito elástico ao
puxar além do limite).

## APIs usadas
- `Component.setScrollable(ScrollDirection dir, ScrollEngine... engine)` — habilita scroll. `engine` é opcional (default: `ScrollEngine` novo). Passe `null` como `dir` para desabilitar.
- `UIConstants.ScrollDirection` — enum com **três** valores: `HORIZONTAL`, `VERTICAL`, `BOTH`.
- `Component.isScrollable()` — se o componente está rolável.
- `Component.scrollable()` — retorna o objeto `Scrollable` (com `.engine`), ou `null`.
- `Component.setShowScrollPosition(boolean)` — mostra/oculta o indicador de posição de scroll.
- `ScrollEngine.setOverscroll(boolean x, boolean y)` — liga/desliga o efeito elástico por eixo. Default: ambos `true`.
- `ScrollEngine.setupOverscroll(float distance, float speedMin, float speedMax)` — ajusta distância e velocidade de retorno do overscroll.
- `ScrollEngine.setScrollListener(ScrollListener listener)` — callback a cada mudança de offset (recebe `vx, vy`).
- `ScrollEngine.scrollTo(float x, float y)` / `scrollToStartSmooth()` / `scrollToEndSmooth()` — reposiciona o scroll.
- `ScrollEngine.setOverscrollEffect(OverscrollEffect effect)` — **estático**: define/desativa (`null`) a curva cinza de borda global; customizável via `new OverscrollEffect().setColor(...).setMaxAlpha(...).setMaxArcHeight(...)`.

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

      Component painel = new Component();
      painel.setLayout(new GridLayout(Position.CENTER, Position.START));
      for (int i = 0; i < 60; i++)
         painel.add(.95f, new Label("Linha " + i));

      painel.setScrollable(ScrollDirection.VERTICAL);   // rola no eixo Y

      // ajustes opcionais no motor de scroll:
      painel.scrollable().engine.setOverscroll(true, false);   // elástico só na vertical
      painel.setShowScrollPosition(true);                       // indicador de posição
      painel.scrollable().engine.setScrollListener((vx, vy) ->
         Debug.log("scrollY = " + vy));                         // reage ao scroll

      add(LAY_FILL, painel);
   }
}
```

> O trecho abaixo mostra **só o miolo**. Para rodar, coloque-o no esqueleto `MainWindow` acima.

## Trecho mínimo
```java
// verificado na fonte: Component.setScrollable (linha ~1027) + ScrollEngine.java
Component painel = new Component();
painel.setLayout(new GridLayout(Position.CENTER, Position.START));
for (int i = 0; i < 60; i++)
   painel.add(.95f, new Label("Linha " + i));

painel.setScrollable(ScrollDirection.VERTICAL);   // rola no eixo Y

// ajustes opcionais no motor de scroll:
painel.scrollable().engine.setOverscroll(true, false);   // elástico só na vertical
painel.setShowScrollPosition(true);                       // indicador de posição
painel.scrollable().engine.setScrollListener((vx, vy) ->
   Debug.log("scrollY = " + vy));                         // reage ao scroll

layout.add(1f, painel);
```

Reposicionando por código:

```java
painel.scrollable().engine.scrollToStartSmooth();   // volta ao topo com animação
painel.scrollable().engine.scrollTo(0, 500);        // salta para offset y=500 px
```

## Nota honesta: não há pull-to-refresh pronto

O framework **não** oferece um widget de "pull-to-refresh" (puxar-para-atualizar) pronto. O que
existe é o efeito de **overscroll** (curva elástica ao puxar além do limite), que é puramente
**visual/físico** — ele não expõe um callback de "soltou após puxar X pixels" para você disparar
um refresh. Se precisar desse comportamento, terá que construí-lo manualmente monitorando o offset
via `setScrollListener` e detectando o overscroll além de um limiar. Para listas grandes com
recarga de dados, prefira `VirtualListView.reloadData()` acionado por um botão ou gesto próprio.

## Aprofunde-se
- Listas enormes já vêm roláveis por padrão (o `VirtualListView` chama `setScrollable(VERTICAL)` internamente): veja [lista-virtual-grande.md](/docs/cookbook/lists/lista-virtual-grande).
