# Cómo hacer un Component desplazable y ajustar el overscroll

> Habilitar scroll en cualquier Component con setScrollable y configurar el efecto elástico de borde.

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

# Cómo hacer un Component desplazable y ajustar el overscroll

**Propósito.** Cualquier `Component` puede convertirse en un contenedor desplazable: basta declarar la dirección con
`setScrollable(ScrollDirection)`. Si el contenido (la suma de las alturas de los hijos) excede el área
visible, el `ScrollEngine` se encarga de pan, momentum, fling y del **overscroll** (efecto elástico al
tirar más allá del límite).

## APIs usadas
- `Component.setScrollable(ScrollDirection dir, ScrollEngine... engine)` — habilita scroll. `engine` es opcional (default: `ScrollEngine` nuevo). Pasa `null` como `dir` para deshabilitar.
- `UIConstants.ScrollDirection` — enum con **tres** valores: `HORIZONTAL`, `VERTICAL`, `BOTH`.
- `Component.isScrollable()` — si el componente está desplazable.
- `Component.scrollable()` — retorna el objeto `Scrollable` (con `.engine`), o `null`.
- `Component.setShowScrollPosition(boolean)` — muestra/oculta el indicador de posición de scroll.
- `ScrollEngine.setOverscroll(boolean x, boolean y)` — activa/desactiva el efecto elástico por eje. Default: ambos `true`.
- `ScrollEngine.setupOverscroll(float distance, float speedMin, float speedMax)` — ajusta distancia y velocidad de retorno del overscroll.
- `ScrollEngine.setScrollListener(ScrollListener listener)` — callback en cada cambio de offset (recibe `vx, vy`).
- `ScrollEngine.scrollTo(float x, float y)` / `scrollToStartSmooth()` / `scrollToEndSmooth()` — reposiciona el scroll.
- `ScrollEngine.setOverscrollEffect(OverscrollEffect effect)` — **estático**: define/desactiva (`null`) la curva gris de borde global; personalizable via `new OverscrollEffect().setColor(...).setMaxAlpha(...).setMaxArcHeight(...)`.

## Ejemplo completo (copiar, pegar y ejecutar)
```java
package com.exemplo;

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

/** Pantalla principal ejecutable. Apunta el launcher (classname) y corre: 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);   // desplaza en el eje Y

      // ajustes opcionales en el motor de scroll:
      painel.scrollable().engine.setOverscroll(true, false);   // elástico solo en la vertical
      painel.setShowScrollPosition(true);                       // indicador de posición
      painel.scrollable().engine.setScrollListener((vx, vy) ->
         Debug.log("scrollY = " + vy));                         // reacciona al scroll

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

> El fragmento de abajo muestra **solo el núcleo**. Para ejecutarlo, colócalo en el esqueleto `MainWindow` de arriba.

## Fragmento mínimo
```java
// verificado en la fuente: Component.setScrollable (línea ~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);   // desplaza en el eje Y

// ajustes opcionales en el motor de scroll:
painel.scrollable().engine.setOverscroll(true, false);   // elástico solo en la vertical
painel.setShowScrollPosition(true);                       // indicador de posición
painel.scrollable().engine.setScrollListener((vx, vy) ->
   Debug.log("scrollY = " + vy));                         // reacciona al scroll

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

Reposicionando por código:

```java
painel.scrollable().engine.scrollToStartSmooth();   // vuelve al tope con animación
painel.scrollable().engine.scrollTo(0, 500);        // salta al offset y=500 px
```

## Nota honesta: no hay pull-to-refresh listo

El framework **no** ofrece un widget de "pull-to-refresh" (tirar-para-actualizar) listo. Lo que
existe es el efecto de **overscroll** (curva elástica al tirar más allá del límite), que es puramente
**visual/físico** — no expone un callback de "soltó después de tirar X píxeles" para que dispares
un refresh. Si necesitas ese comportamiento, tendrás que construirlo manualmente monitoreando el offset
via `setScrollListener` y detectando el overscroll más allá de un umbral. Para listas grandes con
recarga de datos, prefiere `VirtualListView.reloadData()` accionado por un botón o gesto propio.

## Profundiza
- Las listas enormes ya vienen desplazables por defecto (el `VirtualListView` llama a `setScrollable(VERTICAL)` internamente): mira [lista-virtual-grande.md](/docs/cookbook/lists/lista-virtual-grande).
