# messagebox — `n_pbt_messagebox` #

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

> Cuadro de diálogo modal con tema, de resultado síncrono: el sustituto directo del `MessageBox()` de PowerBuilder, con texto enriquecido, botones libres, iconos y casilla de verificación.

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

## De un vistazo #

Objeto | `n_pbt_messagebox` — no visual: nada que colocar en la ventana
Sirve para | Plantear una pregunta o anunciar un resultado, en lugar del `MessageBox()` nativo, rígido y sin tema
Resultado | Síncrono: `of_show()` bloquea y devuelve el índice del botón pulsado

A diferencia de los componentes visuales, este objeto no se inserta en una ventana: se crea, configura, muestra y destruye.

    n_pbt_messagebox lnv_mb

    lnv_mb = create n_pbt_messagebox
    // ... configuracion ...
    destroy lnv_mb

## Inicio rápido #

    n_pbt_messagebox lnv_mb

    lnv_mb = create n_pbt_messagebox

    lnv_mb.is_title   = "Eliminación"
    lnv_mb.is_icon    = lnv_mb.ICON_WARNING
    lnv_mb.is_message = "¿Eliminar definitivamente [b]12 carpetas[/b]?[br][br]Esta acción es irreversible."

    lnv_mb.of_add_button(/*texto*/ "Eliminar", /*predeterminado*/ true,  /*cancelacion*/ false)   // -> 1
    lnv_mb.of_add_button(/*texto*/ "Cancelar",   /*predeterminado*/ false, /*cancelacion*/ true)    // -> 2

    if lnv_mb.of_show(/*hwnd*/ Handle(this)) = 1 then
        of_supprimer()
    end if

    destroy lnv_mb

`of_show` espera la respuesta del usuario: la línea siguiente no se ejecuta hasta después del clic, exactamente igual que con `MessageBox()`.

## Propiedades #

Deben establecerse antes de `of_show`.

Propiedad | Tipo | Predeterminado | Función
--- | --- | --- | ---
`is_title` | string | `""` | Título mostrado en el encabezado del cuadro
`is_message` | string | `""` | Cuerpo del mensaje. Acepta el texto enriquecido con etiquetas (`[b]`, `[i]`, `[br]`, `[accent]`, `[picture=…]`…)
`is_instruction` | string | `""` | Instrucción principal: la pregunta en sí, mostrada más grande encima del mensaje. Título / instrucción / mensaje es la anatomía que hace un diálogo legible de un vistazo — «¿Eliminar 42 filas?» y luego «Esta acción es definitiva» — en lugar de un bloque uniforme. Acepta el marcado
`is_icon` | string | `""` | Icono: una constante `ICON_*`, o su propia imagen (ruta de archivo, o recurso de DLL `mi.dll:NOMBRE`)
`is_checkbox` | string | `""` | Texto de una casilla de verificación opcional, del estilo «no volver a preguntar» (`""` = sin casilla)
`ib_checked` | boolean | `false` | Estado inicial de la casilla (el estado final se lee con `of_checked()`)
`ib_input` | boolean | `false` | Añade un campo de entrada con el tema aplicado (renombrar, motivo, comentario), de modo que una aplicación ya no necesita una ventana hecha a mano que no sigue ni el tema ni el sentido de lectura. Se relee con `of_input_value()` tras `of_show`. Todo en una llamada: `of_prompt`
`is_input_label` | string | `""` | Etiqueta encima del campo (`""` = ninguna). Necesita `ib_input`
`is_input_value` | string | `""` | Contenido inicial del campo. Está seleccionado al abrirse: escribir lo sustituye, como en todo diálogo de renombrado
`is_input_placeholder` | string | `""` | Indicación mostrada mientras el campo está vacío. No es un valor: si el usuario no escribe nada, no se devuelve nada
`ib_input_password` | boolean | `false` | Enmascara los caracteres escritos
`ib_input_required` | boolean | `false` | El botón predeterminado permanece desactivado mientras el campo esté vacío. Dejar enviar para reprender después no sirve a nadie; el botón de cancelar sigue accesible
`ib_buttons_reverse` | boolean | `false` | Orden de los botones: `false` = de izquierda a derecha en el orden de adición; `true` = invertido
`is_position` | string | `POSITION_OWNER` | Centrado: `POSITION_OWNER` (sobre la ventana llamante) o `POSITION_SCREEN` (sobre la pantalla)
`il_min_width` | long | `0` | Anchura mínima en píxeles (`0` = automática)
`il_max_width` | long | `0` | Anchura máxima en píxeles (`0` = automática): el texto se ajusta dentro de este límite
`il_max_height` | long | `0` | Altura máxima en píxeles (`0` = automática): más allá, el cuerpo del mensaje se desplaza en lugar de agrandar la ventana

### Constantes #

Constante | Valor | Uso
--- | --- | ---
`ICON_INFORMATION` | `"information"` | Información neutra
`ICON_WARNING` | `"warning"` | Advertencia, acción arriesgada
`ICON_ERROR` | `"error"` | Fallo, error
`ICON_QUESTION` | `"question"` | Pregunta cerrada
`ICON_SUCCESS` | `"success"` | Confirmación de un éxito
`ICON_NONE` | `"none"` | Ningún icono
`POSITION_OWNER` | `"owner"` | Centrado sobre la ventana llamante
`POSITION_SCREEN` | `"screen"` | Centrado sobre la pantalla

## Métodos #

Método | Función
--- | ---
`of_add_button (string as_texte)` → long | Añade un botón simple. Devuelve su índice a partir de 1
`of_add_button (string as_texte, boolean ab_defaut, boolean ab_annulation)` → long | Lo mismo, marcando el botón como predeterminado (Intro) y/o de cancelación (Esc)
`of_add_button (string as_texte, string as_icone, boolean ab_defaut, boolean ab_annulation)` → long | Lo mismo, con un icono en el botón
`of_add_button_timed (string as_texte, boolean ab_defaut, boolean ab_annulation, long al_secondes_actif, long al_secondes_clic)` → long | Botón con cuenta atrás: permanece desactivado `al_secondes_actif` segundos (con contador visible), y luego se pulsa solo al cabo de `al_secondes_clic` segundos (`0` = temporizador inactivo)
`of_show (long al_hwnd)` → long | Muestra el cuadro modal y devuelve el índice del botón pulsado (`0` = cierre con Esc o con la cruz sin botón de cancelación)
`of_checked ( )` → boolean | Estado de la casilla de verificación en el momento del último `of_show`
`of_input_value ( )` → string | Texto escrito en el último `of_show` (vacío si `ib_input` estaba desactivado)
`of_action ( )` → string | Id de la zona `[action=id]` pulsada en el mensaje, cadena vacía en caso contrario. Tal zona es una elección ofrecida en la propia frase: cierra el diálogo y `of_show` devuelve `0`. Una zona `[hyperlink=url]`, en cambio, se abre en el navegador y deja el diálogo abierto — el llamante está bloqueado en `of_show`, así que un enlace no puede ser una respuesta
`of_info (long al_hwnd, string as_title, string as_message)` → long | Diálogo de una línea, como lo es `MessageBox()`: icono de información y un solo botón Aceptar, devuelve `1`. Las etiquetas vienen de las traducciones de la biblioteca (6 idiomas) en lugar de escribirse en cada aplicación — esa es toda la razón de ser de estos atajos
`of_warning (long al_hwnd, string as_title, string as_message)` → long | Icono de advertencia, un botón Aceptar. Devuelve `1`
`of_error (long al_hwnd, string as_title, string as_message)` → long | Icono de error, un botón Aceptar. Devuelve `1`
`of_success (long al_hwnd, string as_title, string as_message)` → long | Icono de éxito, un botón Aceptar. Devuelve `1`
`of_confirm (long al_hwnd, string as_title, string as_message)` → long | Pregunta + Aceptar / Cancelar. Devuelve `1` = Aceptar, `2` = Cancelar, `0` = cerrado
`of_yes_no (long al_hwnd, string as_title, string as_message)` → long | Pregunta + Sí / No. Devuelve `1` = Sí, `2` = No, `0` = cerrado
`of_yes_no_cancel (long al_hwnd, string as_title, string as_message)` → long | Pregunta + Sí / No / Cancelar. Devuelve `1`, `2`, `3`, o `0` si se cierra
`of_prompt (long al_hwnd, string as_title, string as_message, string as_default)` → string | Pide un valor y devuelve lo escrito, o una cadena vacía si el usuario canceló. Para distinguir una respuesta vacía de una cancelación, use `of_show` + `of_input_value`
`of_reset ( )` | Borra todas las propiedades y los botones añadidos: la misma instancia vuelve a empezar de cero

La etiqueta de un botón acepta el texto enriquecido con etiquetas y el mnemotécnico `&` (`"&Guardar"` subraya la G y lo activa con `Alt+G`); `&&` muestra un ampersand literal.

## El teclado #

Tecla | Efecto
--- | ---
Intro | Activa el botón marcado como predeterminado
Esc | Activa el botón marcado como cancelación; sin botón de cancelación, cierra el cuadro y devuelve `0`
Alt + letra | Activa el botón cuya etiqueta lleva ese mnemotécnico
Tab | Desplaza el foco de un botón a otro
Ctrl + C | Copia el diálogo (título, instrucción, mensaje, etiquetas de los botones) al portapapeles, como todo cuadro de diálogo de Windows — práctico cuando hay que reenviar un error al soporte

> Al abrirse, ningún botón tiene contorno de foco: es intencionado, y es el comportamiento de los cuadros de diálogo modernos de Windows. El borde solo aparece tras una primera pulsación de Tab, es decir, cuando el usuario pasa explícitamente al teclado. Intro y Esc permanecen activos desde el primer segundo, incluso sin foco visible.

## Ejemplos #

### Pregunta cerrada con botón predeterminado #

    n_pbt_messagebox lnv_mb
    long ll_reponse

    lnv_mb = create n_pbt_messagebox

    lnv_mb.is_title   = "Guardar los cambios"
    lnv_mb.is_icon    = lnv_mb.ICON_QUESTION
    lnv_mb.is_message = "La carpeta se ha modificado. ¿Desea guardar antes de cerrar?"

    lnv_mb.of_add_button(/*texto*/ "&Guardar",     /*predeterminado*/ true,  /*cancelacion*/ false)  // 1
    lnv_mb.of_add_button(/*texto*/ "&No guardar", /*predeterminado*/ false, /*cancelacion*/ false) // 2
    lnv_mb.of_add_button(/*texto*/ "Cancelar",          /*predeterminado*/ false, /*cancelacion*/ true)   // 3

    ll_reponse = lnv_mb.of_show(/*hwnd*/ Handle(this))
    destroy lnv_mb

    choose case ll_reponse
        case 1 ; of_enregistrer() ; Close(parent)
        case 2 ; Close(parent)
        case else ; // 3 o 0 : no se cierra
    end choose

### Mensaje enriquecido e icono #

    lnv_mb.is_title   = "Importación finalizada"
    lnv_mb.is_icon    = lnv_mb.ICON_SUCCESS
    lnv_mb.is_message = "[b]1 240 líneas[/b] integradas.[br][br]" &
                      + "[accent]18 duplicados[/accent] se han ignorado."

    lnv_mb.of_add_button(/*texto*/ "Aceptar", /*predeterminado*/ true, /*cancelacion*/ false)
    lnv_mb.of_show(/*hwnd*/ Handle(this))

### Casilla «no volver a preguntar» #

    n_pbt_messagebox lnv_mb

    lnv_mb = create n_pbt_messagebox

    lnv_mb.is_title    = "Eliminación"
    lnv_mb.is_icon     = lnv_mb.ICON_WARNING
    lnv_mb.is_message  = "¿Eliminar las líneas seleccionadas? Esta acción es irreversible."
    lnv_mb.is_checkbox = "No volver a preguntar"
    lnv_mb.ib_checked  = false

    lnv_mb.of_add_button(/*texto*/ "Eliminar", /*predeterminado*/ true,  /*cancelacion*/ false)
    lnv_mb.of_add_button(/*texto*/ "Cancelar",   /*predeterminado*/ false, /*cancelacion*/ true)

    if lnv_mb.of_show(/*hwnd*/ Handle(this)) = 1 then
        of_supprimer()
        // Memorizar la eleccion del usuario
        ib_confirmer_suppression = not lnv_mb.of_checked()
    end if

    destroy lnv_mb

### Botón con cuenta atrás #

    lnv_mb.is_title   = "Reinicio"
    lnv_mb.is_icon    = lnv_mb.ICON_INFORMATION
    lnv_mb.is_message = "La aplicación se va a reiniciar para aplicar la actualización."

    // "Continuar" permanece atenuado 3 segundos (se muestra un contador)
    lnv_mb.of_add_button_timed(/*texto*/ "Continuar", /*predeterminado*/ true, /*cancelacion*/ false, &
                               /*segundos_activo*/ 3, /*segundos_clic*/ 0)

    // "Mas tarde" se pulsa solo al cabo de 10 segundos
    lnv_mb.of_add_button_timed(/*texto*/ "Más tarde", /*predeterminado*/ false, /*cancelacion*/ true, &
                               /*segundos_activo*/ 0, /*segundos_clic*/ 10)

    lnv_mb.of_show(/*hwnd*/ Handle(this))

### Mensaje largo: limitar el tamaño #

    // Un texto voluminoso : el cuadro esta limitado y el cuerpo se desplaza
    lnv_mb.is_title      = "Notas de la versión"
    lnv_mb.is_message    = ls_notes
    lnv_mb.il_max_width  = 480
    lnv_mb.il_max_height = 320

    lnv_mb.of_add_button(/*texto*/ "Cerrar", /*predeterminado*/ true, /*cancelacion*/ true)
    lnv_mb.of_show(/*hwnd*/ Handle(this))

### Reutilizar una instancia #

    // Una instancia de ventana, varios dialogos : of_reset entre cada llamada
    inv_mb.of_reset()      // borra las propiedades Y los botones anteriores

    inv_mb.is_title   = "Segundo diálogo"
    inv_mb.is_message = "Cada of_reset vuelve a empezar con un cuadro en blanco."
    inv_mb.of_add_button(/*texto*/ "Aceptar", /*predeterminado*/ true, /*cancelacion*/ false)
    inv_mb.of_show(/*hwnd*/ Handle(this))

## Buenas prácticas #

- Llame siempre a `of_reset()` antes de reconfigurar una instancia reutilizada: sin ello, los botones del diálogo anterior se suman a los nuevos.
- Marque sistemáticamente un botón predeterminado y un botón de cancelación: quien usa el teclado espera Intro y Esc.
- Compruebe el valor de retorno `0`: significa que el cuadro se ha cerrado sin elección (cruz o Esc). Trátelo como una cancelación.
- Pase `Handle(this)` (o `Handle(parent)`) como ventana llamante: el cuadro se centra sobre ella y la modalidad afecta a la ventana correcta.
- Reserve el rojo e `ICON_ERROR` para los errores reales; una confirmación corriente merece `ICON_QUESTION`.
- Para una información que no requiere ninguna respuesta, prefiera una notificación no bloqueante: véase toaster.

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