# tab — `u_pbt_tab` #

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

> Pestañas modernas que alojan controles reales de PowerBuilder: banda reposicionable, iconos por pestaña, pestañas que se pueden cerrar, reordenación con el ratón.

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

## De un vistazo #

Userobject | `u_pbt_tab`
Clase de items | `n_pbt_tab_page` (una página = una pestaña)
Sirve para | Sustituir un `tab` de PowerBuilder por una banda con tema, conservando sus pantallas existentes como páginas
Opciones opt-in | `ib_reorderable`

El argumento clave: una página no es una maqueta HTML, es un `dragobject` de PowerBuilder: un userobject, una DataWindow, un grupo de controles o incluso otro componente PBToolboxAI. El componente se encarga de la colocación, del redimensionamiento y de mostrar u ocultar al cambiar de pestaña. Consulte Alojar controles reales de PowerBuilder.

## Inicio rápido #

    // event open de la ventana : cada pagina es un control PB ya puesto en la ventana
    uo_onglets.of_add_page("clients",  "Clientes",  uo_page_clients)
    uo_onglets.of_add_page("factures", "Facturas", uo_page_factures, /*cerrable*/ true)

    // Un icono por pestana, puesto sobre el handle de la pagina
    uo_onglets.of_item("clients").is_icon  = "mono:img\clients.svg"
    uo_onglets.of_item("factures").is_icon = "mono:img\invoice.svg"

    uo_onglets.of_select_page("clients")

    // event ue_selection_changed de uo_onglets : (string as_from_id, string as_id)
    of_charger_page(as_id)

## Propiedades #

Propiedad | Tipo | Predeterminado | Función
--- | --- | --- | ---
`is_position` | string | `"top"` | Colocación de la banda: `top`, `bottom` (banda horizontal), `start`, `end` (banda vertical). Constantes `POSITION_TOP`, `POSITION_BOTTOM`, `POSITION_START`, `POSITION_END`. `START`/`END` son lógicas y siguen el sentido de escritura
`is_overflow_mode` | string | `"menu"` | Lo que hace una banda demasiado pequeña para sus pestañas. `menu` (predeterminado) mantiene legible cada pestaña: las que ya no caben salen de la banda y el botón `···` las enumera todas, con la pestaña activa siempre visible. `compact` las mantiene todas en la banda y las deja reducirse, hasta el espacio que necesita un texto truncado. Constantes `OVERFLOW_MENU`, `OVERFLOW_COMPACT`
`ib_reorderable` | boolean | `false` | Opt-in: el usuario puede reordenar las pestañas arrastrando su encabezado con el ratón (activa `ue_tab_reordered`)
`ib_veto_selection` | boolean | `true` | Preguntar antes de cambiar de pestaña activa (activa `ue_selection_changing`, que puede rechazarlo). Activo de forma predeterminada: no programar nada deja pasar siempre. Póngalo a `false` para suprimir la ida y vuelta hasta PowerBuilder (~35 ms) donde se notaría
`ib_veto_close` | boolean | `true` | Preguntar antes de cerrar una pestaña que se puede cerrar (activa `ue_tab_closing`, que puede rechazarlo). Activo de forma predeterminada, misma regla
`ib_context_menu` | boolean | `true` | Menú contextual integrado en el encabezado de una pestaña: Cerrar, Cerrar las demás, Cerrar a la derecha, Cerrar todas, atenuados cuando no tienen objetivo. Activo de forma predeterminada. El clic derecho selecciona primero la pestaña: si la aplicación rechaza el cambio, no se abre ningún menú. Cada cierre pasa por `ue_tab_closing`, una pregunta por página: la que tiene trabajo sin guardar rechaza por sí sola y permanece abierta mientras sus vecinas se van. A `false` para mostrar su propio menú desde `ue_rclicked`
`is_theme_style` | string | `fluent` | Estilo visual del componente (constantes `THEME_STYLE_*`)
`is_theme_mode` | string | `light` | Variante clara u oscura (constantes `THEME_MODE_*`)
`il_theme_accent` | long | `-1` | Color de acento de este componente (`-1` = acento del tema)
`is_tooltip` | string | `""` | Tooltip simple mostrado al pasar el ratón por el componente
`is_super_tooltip_title` | string | `""` | Título del tooltip enriquecido (prevalece sobre `is_tooltip`)
`is_super_tooltip_text` | string | `""` | Texto del tooltip enriquecido (se acepta el marcado enriquecido)
`is_super_tooltip_image` | string | `""` | Imagen del tooltip enriquecido

### Propiedades de una página — `n_pbt_tab_page` #

Obtenidas mediante `of_item(id)`, se modifican en caliente, sin reconstruir la banda.

Propiedad | Tipo | Predeterminado | Función
--- | --- | --- | ---
`is_title` | string | `""` | Etiqueta de la pestaña. Admite el texto enriquecido con etiquetas
`is_icon` | string | `""` | Icono de la pestaña (formas admitidas: ruta, `mono:`, `tint:`, recurso de una DLL). Vacío = ningún icono
`ib_enabled` | boolean | `true` | `false` atenúa la pestaña e impide que el usuario la seleccione
`ib_visible` | boolean | `true` | `false` oculta la pestaña sin eliminar la página; ocultar la pestaña activa activa una vecina
`ib_closable` | boolean | `false` | Aspa de cierre en esta pestana. Rara vez se conoce al anadir la pagina: pasa a `true` en cuanto se guarda el documento que contiene, y a `false` mientras se ejecuta un proceso dentro
`is_shortcut` | string | `""` | Atajo de teclado que activa esta pestaña: `"Ctrl+2"`, `"Alt+F"`, `"F6"`… Responde esté donde esté el usuario en la ventana, incluida la página alojada — el atajo lo tiene la DLL, no la barra de pestañas. Seleccionar así es un clic: se plantea `ue_selection_changing` y `ue_selection_changed` le sigue. `""` lo quita
`is_tooltip` | string | `""` | Tooltip simple mostrado al pasar el ratón por el item
`is_super_tooltip_title` | string | `""` | Título del tooltip enriquecido del item (prevalece sobre `is_tooltip`)
`is_super_tooltip_text` | string | `""` | Texto del tooltip enriquecido del item (se acepta el marcado enriquecido)
`is_super_tooltip_image` | string | `""` | Imagen del tooltip enriquecido del item

## Métodos #

Método | Función
--- | ---
`of_add_page (string as_id, string as_title, dragobject ado_page)` | Añade una pestaña que no se puede cerrar, sin icono, y aloja el control
`of_add_page (string as_id, string as_title, dragobject ado_page, boolean ab_closable)` | Ídem, con aspa de cierre si `ab_closable`
`of_add_page (string as_id, string as_title, dragobject ado_page, boolean ab_closable, string as_icon)` | Ídem, con el icono puesto en la misma llamada (la pestaña aparece completa de entrada)
`of_insert_page (string as_id, string as_title, dragobject ado_page, integer ai_index)` | Añade la página y después la desplaza a la posición `ai_index`
`of_insert_page (string as_id, string as_title, dragobject ado_page, boolean ab_closable, string as_icon, integer ai_index)` | Ídem, diciendo lo que dice `of_add_page` : si la pestaña se cierra, y su icono
`of_move_page (string as_id, integer ai_index)` | Desplaza una pestaña existente; su página alojada y la selección se conservan
`of_select_page (string as_id)` | Activa una pestaña — estrictamente equivalente a un clic del usuario (pasa por `ue_selection_changing` y luego `ue_selection_changed`)
`of_remove_page (string as_id)` | Retira la pestaña y devuelve el control a su ventana de origen, oculto. El control sigue siendo utilizable y se puede alojar en otro sitio
`of_selected_key ( )` | Identificador de la pestaña activa, `""` si no hay ninguna
`of_get_layout ( )` | Vuelve a leer la disposición actual en JSON: el orden de las pestañas y la visibilidad de cada una. Guárdela (archivo, base de datos, registro) y devuélvala con `of_set_layout` en el siguiente arranque. El mismo par lleva los mismos nombres en todos los componentes reorganizables
`of_set_layout (string as_layout_json)` | Restaura una disposición leída con `of_get_layout` o recibida con `ue_layout_changed`. Lo que la disposición no nombra conserva su lugar al final: una disposición de ayer no debe hacer desaparecer lo añadido desde entonces. Aplicarla no genera ningún event — usted la ha proporcionado
`of_item (string as_id)` | Handle `n_pbt_tab_page` de la página (creado en el primer acceso)
`of_refresh_page (string as_id)` | Actualiza la representación de una página construida fuera de pantalla, sin parpadeo
`of_relayout ( )` | Vuelve a publicar la zona de página para que el control alojado se recoloque (útil tras una visualización diferida)
`of_preload_icons (string as_icons[])` | Precalienta un lote de iconos al arrancar: una pestaña abierta más tarde muestra su icono al instante
`of_reset ( )` | Vacía la banda: cada página alojada se devuelve a su ventana de origen y después el componente vuelve a su estado inicial
`of_set_redraw (boolean)` | Agrupa una ráfaga de modificaciones en una sola representación
`of_save_as_png (string)` · `of_save_as_jpg (string)` | Exporta la representación como imagen

> `of_add_page` devuelve un código negativo si el identificador está vacío, ya se utiliza, o si el control pasado no es válido.

Cuando hay demasiadas pestañas — se reducen para caber en la banda, pero nunca por debajo de un ancho legible: el icono y la cruz de cierre ocupan su sitio además del título, nunca en su lugar. Las que ya no caben salen de la banda, y en su extremo aparece un botón `···`: abre la lista de todas las pestañas visibles, con la activa marcada, y la elección cambia a ella. La pestaña activa siempre permanece visible. La banda no se desplaza — ese botón es el camino hacia las pestañas fuera del marco. Nada que programar, el comportamiento es automático.

## Eventos #

Evento | Se activa cuando
--- | ---
`ue_selection_changed (string as_from_id, string as_id)` | La pestaña activa ha cambiado — mediante un clic o mediante `of_select_page`. Mismos argumentos que `ue_selection_changing`: la pregunta y su resultado se leen igual, y `as_from_id` es la página abandonada
`ue_tab_closed (string as_id)` | El usuario hace clic en el aspa de una pestaña que se puede cerrar. La pestaña ya se ha retirado y la página se ha devuelto a su ventana
`ue_tab_reordered (string as_id, integer ai_index)` | El usuario ha terminado de arrastrar un encabezado. `ai_index` es la nueva posición, a partir de 1
`ue_layout_changed (string as_layout_json)` | La disposición ha cambiado — el usuario ha reorganizado algo, o su propio código. Lleva la disposición entera, no solo lo que se movió: conservarla es una sola asignación
`ue_selection_changing (string as_from_id, string as_id)` → boolean | Cancelable, se plantea antes de que cambie la pestaña activa. Lanzado de forma predeterminada; `ib_veto_selection = false` lo suprime. Devuelva `false` para mantener al usuario en su página
`ue_tab_closing (string as_id)` → boolean | Cancelable, se plantea antes del cierre de una pestaña que se puede cerrar. Lanzado de forma predeterminada; `ib_veto_close = false` lo suprime. Nombrado para hacer pareja con `ue_tab_closed`: closing / closed. Devuelva `false` para mantener abierta la pestaña (y su página alojada)
`ue_ready ( )` | El componente ha terminado de cargarse; todo lo enviado antes se ha reproducido
`ue_runtime_missing ( )` | El runtime WebView2 está ausente: el componente permanece vacío
`ue_bg_color (long al_color)` | El componente ha calculado el color de fondo de su tema; el userobject ya lo ha adoptado (`backcolor`)

## Ejemplos #

### Un archivador de páginas de negocio #

    // event open : los userobjects de pagina ya estan puestos en la ventana
    uo_onglets.of_set_redraw(false)

    uo_onglets.of_add_page(/*id*/ "identite", /*titulo*/ "Identidad",  /*pagina*/ uo_identite)
    uo_onglets.of_add_page(/*id*/ "adresses", /*titulo*/ "Direcciones",  /*pagina*/ uo_adresses)
    uo_onglets.of_add_page(/*id*/ "compta",   /*titulo*/ "Contabilidad", /*pagina*/ uo_compta)

    uo_onglets.of_item("identite").is_icon = "mono:img\user.svg"
    uo_onglets.of_item("adresses").is_icon = "mono:img\map.svg"
    uo_onglets.of_item("compta").is_icon   = "mono:img\euro.svg"

    uo_onglets.of_set_redraw(true)
    uo_onglets.of_select_page("identite")

### Documentos que se pueden cerrar, abiertos bajo demanda #

    // Abrir un documento en una nueva pestana que se pueda cerrar
    uo_onglets.of_add_page(/*id*/ ls_cle, /*titulo*/ ls_libelle, /*pagina*/ uo_editeur, &
                           /*cerrable*/ true, /*icono*/ "mono:img\doc.svg")
    uo_onglets.of_select_page(ls_cle)

    // event ue_tab_closed de uo_onglets : (string as_id)
    // La pestana ya se ha retirado ; solo queda liberar sus datos de negocio.
    of_liberer_document(as_id)

### Reordenación con el ratón #

    uo_onglets.ib_reorderable = true

    // event ue_tab_reordered de uo_onglets : (string as_id, integer ai_index)
    // ai_index = nueva posicion, a partir de 1 : se memoriza el orden elegido.
    of_enregistrer_ordre(as_id, ai_index)

### Banda vertical #

    // Banda de pestanas en el lado izquierdo, util cuando las etiquetas son largas
    uo_onglets.is_position = uo_onglets.POSITION_START

### Atenuar u ocultar una pestaña según los permisos #

    // El camino pasa siempre por el handle de la pagina
    uo_onglets.of_item("compta").ib_enabled = of_a_le_droit("compta")
    uo_onglets.of_item("audit").ib_visible  = gb_mode_expert

### Reaccionar a la selección #

    // event ue_selection_changed de uo_onglets : (string as_from_id, string as_id)
    choose case as_id
        case "identite" ; uo_identite.of_rafraichir()
        case "adresses" ; uo_adresses.of_rafraichir()
        case "compta"   ; uo_compta.of_rafraichir()
    end choose

### Rechazar un cambio de pestaña o un cierre #

    // Las dos preguntas se plantean de forma PREDETERMINADA: nada que activar. Estas
    // lineas hacen lo contrario, las suprimen cuando arbitrar no sirve.
    uo_onglets.ib_veto_selection = false
    uo_onglets.ib_veto_close     = false

    // event ue_selection_changing de uo_onglets : (string as_from_id, string as_id)
    // Devolver FALSE mantiene al usuario en la pagina que abandona.
    if of_page_modifiee(as_from_id) then
        if MessageBox("Modificaciones", "¿Salir sin guardar?", Question!, YesNo!) = 2 then
            return false
        end if
    end if
    return true

    // event ue_tab_closing de uo_onglets : (string as_id)
    // Devolver FALSE mantiene abiertos la pestana y su pagina alojada.
    return not of_page_modifiee(as_id)

### Volver a empezar con una banda vacía #

    // of_reset devuelve CADA pagina alojada a su ventana de origen antes de vaciar
    uo_onglets.of_reset()
    uo_onglets.of_add_page("accueil", "Inicio", uo_accueil)

## Buenas prácticas #

- Los controles alojados deben existir antes de la llamada: colóquelos en la ventana en fase de diseño, el componente se encarga de ocultarlos y recolocarlos.
- Cargue las páginas una sola vez en el `open` y después controle únicamente la selección: es más rápido que añadir y retirar páginas continuamente.
- Llame a `of_preload_icons` al arrancar si sus pestañas llevan iconos: de lo contrario, la primera apertura de una pestaña oculta muestra un breve retardo antes del icono.
- Las dos preguntas se plantean de forma predeterminada: un evento dejado vacío autoriza siempre la acción, no tiene nada que hacer. Desactívelas con `ib_veto_selection` / `ib_veto_close` a `false` si arbitrar no le sirve, porque cada pregunta cuesta una ida y vuelta hasta PowerBuilder.
- Un control alojado es una ventana nativa: se dibuja por encima de la capa web. Ningún efecto visual del componente puede pasar por delante de él.
- No olvide `of_reset()` antes de reconstruir una banda: sin él, reutilizar un identificador de página falla y los controles anteriores permanecen visibles.
- Para zonas redimensionables y desacoplables en lugar de páginas exclusivas, prefiera dockcontainer.

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