# toaster — `n_pbt_toaster` #

← Referencia de componentes · Índice de la guía

> Notificaciones «toast» en una esquina de la pantalla: un mensaje que aparece, informa y desaparece sin bloquear al usuario ni interrumpir lo que está escribiendo.

> ▶ Verlo en vivo — Aplicación de demostración, mosaico Toaster: la vista previa, el código que lo genera y esta página, uno al lado del otro.

## De un vistazo #

Objeto | `n_pbt_toaster` — no visual: no hay nada que colocar en la ventana
Sirve para | Confirmar una acción realizada con éxito, señalar una advertencia o un error, sin detener el trabajo en curso
Retorno | No bloqueante: `of_show()` devuelve el control de inmediato; las reacciones del usuario vuelven por eventos

El toast es una ventana independiente: flota por encima de su aplicación (o de toda la pantalla) y se cierra solo.

    n_pbt_toaster lnv_toast

    lnv_toast = create n_pbt_toaster
    // ... configuracion ...
    destroy lnv_toast

## Inicio rápido #

    n_pbt_toaster lnv_toast

    lnv_toast = create n_pbt_toaster

    lnv_toast.ipo_owner = this                       // ventana a la que se engancha el toast
    lnv_toast.is_kind   = lnv_toast.KIND_SUCCESS     // icono verde + filete de exito
    lnv_toast.is_text   = "Sus modificaciones se han [b]guardado[/b]."
    lnv_toast.of_show()

    destroy lnv_toast

Todo se configura mediante propiedades, y luego `of_show()` — que no acepta ningún argumento — hace aparecer la notificación.

## Propiedades #

Deben establecerse antes de `of_show`.

Propiedad | Tipo | Predeterminado | Función
--- | --- | --- | ---
`is_text` | string | `""` | Cuerpo del mensaje. Admite el texto enriquecido con etiquetas
`is_kind` | string | `KIND_INFO` | Nivel: gobierna el icono y el color del filete. Constantes `KIND_*`
`is_position` | string | `POSITION_BOTTOM_RIGHT` | Esquina de anclaje. Constantes `POSITION_*`
`ib_screen` | boolean | `false` | `false` = anclado a la esquina de la ventana; `true` = anclado a la esquina de la pantalla, flotando por encima de todo
`il_timeout` | long | `TIMEOUT_AUTO` | Tiempo de visualización en milisegundos antes del cierre automático. `TIMEOUT_AUTO` (`-1`, el valor predeterminado) vale 4 s para una información pero hasta el clic para un error — un error que se borra en cuatro segundos es un error perdido. `TIMEOUT_UNTIL_CLICKED` (`0`) hace persistente cualquier toast; una duración explícita se respeta tal cual. Mientras el puntero permanece sobre un toast su cuenta atrás está suspendida, y una barra muestra el tiempo restante
`is_title` | string | `""` | Línea de título en negrita encima del mensaje (toast enriquecido)
`is_image` | string | `""` | Imagen ilustrativa a la izquierda, en lugar del icono de nivel (formas admitidas: ruta, `mono:`, recurso de DLL)
`is_key` | string | `""` | Clave del toast: volver a mostrar la misma clave actualiza el toast ya en pantalla en lugar de abrir un segundo. Es lo que necesita una notificación de progreso («Exportación 3/10» y luego «4/10»): cerrar y reabrir reiniciaría la animación y descolocaría la pila. Déjela vacía para un toast corriente
`il_max_visible` | long | `5` | Número máximo de toasts en pantalla en la misma esquina (1 a 20). Más allá, los siguientes esperan y aparecen a medida que se libera sitio: un bucle de proceso que emite un toast por fila apilaba si no las ventanas fuera de la pantalla
`ipo_owner` | powerobject | — | El objeto visual al que está enganchado el toast (su esquina de ventana sirve de referencia)
`ipo_receiver` | powerobject | — | El objeto visual que recibe los eventos del toast. Déjelo vacío para una notificación sin retorno

### Constantes #

Constante | Valor | Uso
--- | --- | ---
`KIND_INFO` | `"info"` | Información neutra
`KIND_SUCCESS` | `"success"` | Operación realizada con éxito
`KIND_WARNING` | `"warning"` | Advertencia
`KIND_ERROR` | `"error"` | Fallo
`POSITION_TOP_LEFT` | `"top-left"` | Esquina superior izquierda
`POSITION_TOP_CENTER` | `"top-center"` | Arriba, centrado
`POSITION_TOP_RIGHT` | `"top-right"` | Esquina superior derecha
`POSITION_BOTTOM_LEFT` | `"bottom-left"` | Esquina inferior izquierda
`POSITION_BOTTOM_CENTER` | `"bottom-center"` | Abajo, centrado
`POSITION_BOTTOM_RIGHT` | `"bottom-right"` | Esquina inferior derecha (predeterminado)

> ¿Por qué `LEFT`/`RIGHT` aquí y `START`/`END` en el resto? El toast es una ventana del sistema colocada en píxeles de pantalla, no un contenido que siga un sentido de lectura: una esquina de la pantalla no tiene «inicio». Por eso estas constantes siguen siendo deliberadamente físicas y no cambian de lado cuando la aplicación pasa a escritura de derecha a izquierda. Véase Idioma y RTL.

## Métodos #

Método | Función
--- | ---
`of_show ( )` → long | Muestra la notificación construida a partir de las propiedades. Devuelve el identificador del toast (`> 0`), o un valor negativo en caso de error. No bloquea
`of_close ( long al_id )` → long | Cierra un toast todavía visible, designado por el identificador devuelto por `of_show`. Devuelve `0`, o `-5` si el identificador está vacío o designa un toast que ya ha desaparecido
`of_reset ( )` | Devuelve todas las propiedades de contenido a su valor predeterminado y borra los botones (`ipo_owner` e `ipo_receiver` se conservan: son conexiones, no contenido)
`of_process_events ( )` | Vacía la cola de retornos del toast y activa los eventos `ue_toast_*` correspondientes — véase más abajo
`of_add_button (string as_key, string as_label {, string as_image })` → long | Añade un botón de acción (3 como máximo) y devuelve cuántos hay. Un clic emite `ue_toast_action(id, clave)`; `of_reset` borra los botones. Una etiqueta puede contener cualquier carácter — una coma, un signo igual — sin ser cortada

Varios toasts mostrados en la misma esquina se apilan automáticamente. Cada uno lleva un aspa de cierre; el identificador devuelto por `of_show` permite distinguirlos en los eventos y cerrarlos desde su código.

La cuenta atrás se detiene mientras el puntero está sobre el toast: una notificación no debe desvanecerse ante los ojos de quien la lee. Una fina barra en la parte inferior muestra el tiempo restante — y explica así su desaparición. Un clic en el cuerpo responde y cierra, en ambos modos de alojamiento.

Una notificación creada con `il_timeout = 0` permanece en pantalla mientras nadie la cierre: conserve su identificador para poder retirarla cuando termine la tarea que anuncia.

    long ll_toast

    // Notificacion persistente : permanecera visible hasta of_close.
    inv_toaster.is_text = "Exportación en curso..."
    inv_toaster.il_timeout = /*ms, 0 = sin cierre automatico*/ 0
    ll_toast = inv_toaster.of_show()

    // ... procesamiento largo ...

    inv_toaster.of_close(/*id*/ ll_toast)

## Eventos — el toast le responde #

Una notificación no es un simple «mostrar y olvidar»: puede decirle que se ha hecho clic en ella, que se ha elegido un botón de acción, o que se ha cerrado.

Como el toast vive en una ventana independiente, la conexión se hace en tres pasos.

1. Designar el objeto visual destinatario:

    inv_toaster.ipo_owner    = this
    inv_toaster.ipo_receiver = this      // este userobject / esta ventana recibira los retornos

> `ipo_receiver` debe ser un objeto visual (ventana o userobject). Un objeto no visual no puede recibir notificaciones del sistema.

2. Declarar en ese objeto visual un event asignado a `pbm_custom02`, que vacía la cola:

    // event ue_toast_notified, asignado a pbm_custom02
    if IsValid(inv_toaster) then inv_toaster.of_process_events()

3. Tratar los eventos activados en el toaster:

Evento | Se activa cuando
--- | ---
`ue_toast_clicked (string as_id)` | Se hace clic en el cuerpo del toast (no en un botón)
`ue_toast_action (string as_id, string as_action)` | Se hace clic en un botón de acción; `as_action` contiene la clave pasada a `of_add_button`
`ue_toast_dismissed (string as_id)` | El toast se cierra: tiempo agotado, aspa de cierre, o después de una acción

Sin `ipo_receiver`, la notificación aparece y desaparece sin devolver nunca nada — es el modo más sencillo, perfecto para una simple confirmación.

## Ejemplos #

### Los cuatro niveles #

    inv_toaster.of_reset()
    inv_toaster.is_kind = inv_toaster.KIND_INFO
    inv_toaster.is_text = "Operación terminada."
    inv_toaster.of_show()

    inv_toaster.of_reset()
    inv_toaster.is_kind = inv_toaster.KIND_SUCCESS
    inv_toaster.is_text = "Sus modificaciones se han [b]guardado[/b]."
    inv_toaster.of_show()

    inv_toaster.of_reset()
    inv_toaster.is_kind    = inv_toaster.KIND_WARNING
    inv_toaster.il_timeout = 5000                        // un poco mas largo
    inv_toaster.is_text    = "Poco espacio en disco en la unidad C:."
    inv_toaster.of_show()

    inv_toaster.of_reset()
    inv_toaster.is_kind    = inv_toaster.KIND_ERROR
    inv_toaster.il_timeout = 6000
    inv_toaster.is_text    = "No se puede contactar con el servidor."
    inv_toaster.of_show()

### Elegir la esquina, en la ventana o en la pantalla #

    // Anclado a la esquina superior derecha de la VENTANA (predeterminado : sigue a la aplicacion)
    inv_toaster.is_position = inv_toaster.POSITION_TOP_RIGHT
    inv_toaster.ib_screen   = false
    inv_toaster.is_text     = "Anclado a la esquina de la ventana."
    inv_toaster.of_show()

    // Independiente : anclado a la esquina de la PANTALLA, visible aunque la ventana este minimizada
    inv_toaster.is_position = inv_toaster.POSITION_TOP_RIGHT
    inv_toaster.ib_screen   = true
    inv_toaster.is_text     = "Proceso nocturno terminado."
    inv_toaster.of_show()

### Toast enriquecido: título, imagen y duración #

    inv_toaster.of_reset()

    inv_toaster.is_title    = "Copia de seguridad terminada"
    inv_toaster.is_text     = "1 240 ficheros copiados en [b]\\servidor\backup[/b]."
    inv_toaster.is_image    = "mono:img\backup.svg"
    inv_toaster.il_timeout  = 8000                    // permanece 8 segundos
    inv_toaster.of_show()

### Notificación con botones de acción #

    // event open : conectar el retorno una vez por todas
    inv_toaster.ipo_owner    = this
    inv_toaster.ipo_receiver = this

    // Proponer dos acciones ; timeout 0 = el toast espera la decision del usuario
    inv_toaster.of_reset()

    inv_toaster.is_title   = "Actualización disponible"
    inv_toaster.is_text    = "La versión 2.0 está lista para instalarse."
    inv_toaster.il_timeout = 0
    inv_toaster.of_add_button(/*clave*/ "installer", /*etiqueta*/ "Instalar")
    inv_toaster.of_add_button(/*clave*/ "plus_tard", /*etiqueta*/ "Mas tarde")
    inv_toaster.of_show()

    // event ue_toast_notified de la ventana, asignado a pbm_custom02
    if IsValid(inv_toaster) then inv_toaster.of_process_events()

    // event ue_toast_action de inv_toaster : (string as_id, string as_action)
    choose case as_action
        case "installer" ; of_lancer_mise_a_jour()
        case "plus_tard" ; of_reporter(1)
    end choose

### Reaccionar al clic en el mensaje #

    // event ue_toast_clicked de inv_toaster : (string as_id)
    // El usuario ha hecho clic en el cuerpo del toast : abrir la pantalla correspondiente
    Open(w_journal_import)

### Notificar desde un procesamiento largo #

    // Fin de una importacion : informar sin bloquear la pantalla de entrada de datos
    inv_toaster.of_reset()

    if ll_erreurs = 0 then
        inv_toaster.is_kind = inv_toaster.KIND_SUCCESS
        inv_toaster.is_text = "Importación terminada : [b]" + String(ll_lignes) + " líneas[/b] integradas."
    else
        inv_toaster.is_kind    = inv_toaster.KIND_ERROR
        inv_toaster.il_timeout = 0                    // un error debe leerse
        inv_toaster.is_text    = "Importación interrumpida : " + String(ll_erreurs) + " errores."
    end if

    inv_toaster.of_show()

## Buenas prácticas #

- Una instancia persistente por ventana (variable de instancia creada en la apertura) en lugar de una creación/destrucción con cada mensaje: la conexión `ipo_receiver` permanece en su sitio y los retornos llegan.
- Llame a `of_reset()` antes de cada notificación: de lo contrario, el título, la imagen o los botones de la anterior seguirán puestos.
- Reserve `il_timeout = 0` para los mensajes que exigen una decisión (error bloqueante, acción propuesta): un toast que no se va solo acaba resultando molesto.
- Utilice `ib_screen = true` únicamente para lo que deba seguir siendo visible cuando la aplicación está en segundo plano (fin de un procesamiento largo, tarea nocturna).
- Un toast es un mensaje transitorio: si es imprescindible obtener una respuesta antes de continuar, utilice messagebox, que bloquea y devuelve la elección.
- Para un estado permanente en lugar de una notificación, prefiera statusbar.

← Referencia de componentes · Índice de la guía