# Slider

> Selección de un valor float en un intervalo, continuo o en pasos.

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

# Slider

`Slider` permite elegir un valor `float` dentro de un intervalo `[min, max]` arrastrando un knob. Puede ser **continuo** o **discreto** (con pasos, vía `npos`). La orientación se detecta automáticamente: **horizontal** si `ancho ≥ alto`, si no **vertical**. Tocar fuera del knob anima suavemente hasta el punto tocado; arrastrar actualiza el valor continuamente.

> 🟢 · **Paquete:** `dev.crossfly.ui` · **Extiende:** `Component` · **Estilo:** `SliderStyle` (con variante presionada `sliderPressed()`)

## Cuándo usar
- Valores continuos o aproximados: volumen, brillo, rangos, zoom.
- Selección en pocos pasos discretos (ej.: 1 a 5) con `npos`.

## Cuándo NO usar
- Cuando el usuario necesita escribir un número exacto → usa [`textfield`](/docs/ui/widgets/textfield) en modo numérico.

## Apariencia
Una barra en dos segmentos — **relleno** (antes del knob) y **vacío** (después) — más el knob. Las coordenadas de cada segmento (`bx1/by1/bw1/bh1`, `bx2/by2/bw2/bh2`) y del knob (`kx/ky/ks`) se calculan en layout (todas `float`). El grosor de la barra es `thickness * fontHeight`; el knob es `knobSize * fontHeight` (del `SliderStyle`).

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

      Label saida = new Label("Volume: 50");
      add(LAY_AUTO, saida);

      // Slider continuo 0..100 iniciando en 50; onAction se dispara en cada cambio de value
      Slider volume = new Slider(0, 100, 50);
      volume.onAction(c -> saida.setText("Volume: " + (int) volume.value));
      add(LAY_AUTO, volume);
   }
}
```

> Los fragmentos de abajo muestran **solo el núcleo** (lo que va dentro de `onShow()`). Para ejecutarlos, colócalos en el esqueleto `MainWindow` de arriba.

## Uso básico
```java
Slider volume = new Slider(0, 100, 50);                 // continuo, inicia en 50
volume.onAction(c -> System.out.println("valor: " + volume.value));
container.add(LAY_AUTO, volume);
```

## Constructores
| Constructor | Descripción |
|---|---|
| `Slider(float min, float max, float value)` | Slider **continuo**. |
| `Slider(float min, float max, float value, int npos)` | Slider **discreto**: `npos` posiciones (ej.: `5` → pasos en 0%, 25%, 50%, 75%, 100%). |

## Propiedades y personalización
| Propiedad / método | Tipo | Por defecto | Qué hace |
|---|---|---|---|
| `value` | `float` (campo) | ctor | Valor actual (lectura). |
| `min` / `max` | `float` (campo) | ctor | Límites del intervalo. |
| `npos` | `int` (campo) | `0` | Nº de posiciones discretas; `0` = continuo. |
| `setValue(float)` | `void` | — | Define el valor inmediatamente (= `setValue(v, false)`); aplica `clamp` + `snap`. |
| `setValue(float, boolean animated)` | `void` | — | Define el valor con/sin animación; dispara `runActions()`. |

## Callbacks y eventos
| Callback | Cuándo se dispara |
|---|---|
| `onAction(Action)` | En cada cambio de `value` (arrastre, animación de tap y `setValue`). |

## Estados
- **dragging**: mientras el usuario arrastra el knob (`onPan`), `value` se actualiza en cada movimiento.
- **tap-to-jump**: tocar fuera del knob anima hasta el punto tocado (`PositionAnimation`, duración `style.animationDuration`).
- **pressed**: usa el estilo presionado propio (`sliderPressed()`).

### Slider discreto con 5 paradas
```java
Slider nivel = new Slider(0, 100, 50, 5);               // pasos: 0,25,50,75,100
nivel.onAction(c -> System.out.println("nível: " + nivel.value));
container.add(LAY_AUTO, nivel);

nivel.setValue(75, true);   // anima hasta 75 (será snapped al paso más cercano)
```

## Buenas prácticas
- Pasa un `value` inicial dentro de `[min, max]`: el **constructor no hace clamp** (solo `setValue` lo hace).
- Para forzar orientación vertical, dale al slider más alto que ancho en el layout.

## Errores comunes
- Asumir orientación fija: se decide en el layout por la razón ancho/alto.
- Esperar valores continuos con `npos > 0`: en modo discreto el valor siempre está "snapped" al paso.

## Véase también
- Guía del área: [`ui/`](/docs/ui) · Recetas: [`cookbook/`](/docs/cookbook)
- Concepto (layout y responsividad): [`concepts/`](/docs/concepts)
- Componentes relacionados: [`progress`](/docs/ui/widgets/progress)
