# 3. Base común `u_pbt_base` #

← Primeros pasos · Índice · Temas →

Todos los componentes visuales heredan de `u_pbt_base`, que aporta el ciclo de vida, el motor de propiedades, el transporte hacia el componente web y la gestión de errores. Las propiedades en sí — tema y tooltips incluidos — las publica cada componente: su propia página ofrece la lista completa. Nunca se utiliza `u_pbt_base` directamente — se coloca un componente concreto —, pero todo lo que sigue está disponible en todas partes.

## 3.1 El motor de propiedades #

### Asignar #

Todo valor controlable es una variable de instancia pública, asignada directamente:

    uo_progress.id_value   = 42.5
    uo_progress.is_label   = "Importación en curso…"
    uo_progress.ib_animated = true

El prefijo húngaro indica el tipo: `is_` string, `ib_` boolean, `ii_` integer, `il_` long (a menudo un color `RGB()`), `id_` double.

No existe ningún `of_set_xxx` escalar: una propiedad se establece por asignación. Siguen siendo métodos las adiciones, las eliminaciones y las acciones (`of_add_*`, `of_remove_*`, `of_select_*`, `of_reset`, `of_set_layout`…).

### Releer #

La lectura devuelve el último valor establecido (caché del lado de PowerBuilder):

    if uo_progress.id_value >= 100 then …

Un componente web no puede consultarse de forma síncrona: por tanto, esa caché se actualiza mediante los eventos. Cada vez que el componente mueve una propiedad por sí mismo —el usuario sigue un enlace, amplía con la rueda, pliega la cinta, escribe texto—, el evento que le avisa actualiza la propiedad de paso. La relectura devuelve entonces el estado real, y el nuevo valor ya está puesto cuando se ejecuta el código del evento.

Lo mismo ocurre con los ítems: tras un clic del usuario, `of_item(...)` relee lo que hay en pantalla: la entrada seleccionada, la sección plegada, el botón marcado.

Una propiedad a la que no acompaña ningún evento sí permanece con el último valor que usted estableció.

### Agrupar las modificaciones #

Una ráfaga de asignaciones provoca otras tantas representaciones. `of_set_redraw` las fusiona en una sola:

    uo_grid.of_set_redraw(false)
    … veinte asignaciones y of_add_* …
    uo_grid.of_set_redraw(true)     // UN solo repintado

Llame siempre a ambas (el `true` final no es opcional).

## 3.2 Los items #

Un componente con contenido (pestañas, botones, paneles, mosaicos, secciones…) expone sus elementos mediante handles tipados, obtenidos desde el componente o desde su elemento padre.

### Añadir #

La adición devuelve el handle del elemento creado:

    n_pbt_tab_page lnv_page

    uo_tab.of_add_page("clients", "Clientes", uo_page_clients)
    lnv_page = uo_tab.of_item("clients")
    lnv_page.is_icon = "img\clients.png"

### Recuperar y modificar #

`of_item(id)` — o la factoría del nivel correspondiente — devuelve el handle de un elemento existente; sus propiedades se establecen igual que las de un componente:

    uo_toolbar.of_bar("main").of_item("save").ib_enabled = false
    uo_tab.of_item("clients").is_title = "Clientes (128)"

### Jerarquías: un identificador solo es único dentro de su elemento padre #

Un componente de varios niveles no expone ningún atajo hacia la hoja: la ruta completa es obligatoria, lo que garantiza que ningún identificador resulte ambiguo.

    // Cinta de opciones: pestana > grupo > control > entrada de menu
    uo_ribbon.of_tab("home").of_group("clipboard").of_item("paste").ib_enabled = false

Los eventos llevan asimismo la ruta completa:

    // event ue_clicked de uo_toolbar: (string as_bar, string as_id)
    choose case as_bar + "/" + as_id
        case "main/save" ; of_enregistrer()
    end choose

### Eventos de item #

No existen eventos de item genéricos en el ancestro: un identificador de hoja por sí solo sería ambiguo en cuanto los items se anidan (una toolbar tiene varias barras, un tilesbox varios grupos…). Por eso cada componente declara sus propios eventos de item, con la ruta completa: `ue_item_selected (as_section, as_id)` para la listbar, `ue_tile_clicked (as_group, as_id)` para el tilesbox, `ue_clicked (as_bar, as_id)` para la toolbar…

Consulte la página del componente: ahí figura la lista exacta.

## 3.3 Eventos comunes a todos los componentes #

Evento | Se activa cuando
--- | ---
`ue_ready ( )` | El componente ha terminado de cargarse; todo lo enviado antes se ha reproducido
`ue_runtime_missing ( )` | El runtime WebView2 está ausente — véase Instalación
`ue_bg_color (long al_color)` | El componente ha calculado el color de fondo de su tema; el userobject ya ha adoptado ese color (`backcolor`), le corresponde a usted ajustar la ventana si es necesario

> Los comandos enviados antes de `ue_ready` no se pierden: se ponen en cola y se reproducen en orden. Por tanto, puede configurarlo todo ya en el `constructor` o en el `open`.

    // event ue_bg_color: ajustar la ventana al fondo del componente
    parent.backcolor = al_color

## 3.4 Propiedades y eventos opcionales (opt-in) #

Algunas funcionalidades no están activadas de forma predeterminada: solo las publican los componentes en los que tienen sentido, y hay que solicitarlas.

### Altura automática — `ib_auto_height` #

El componente mide su altura ideal y redimensiona el userobject; el evento `ue_auto_height(al_height)` le permite reposicionar los controles vecinos.

    uo_entete.ib_auto_height = true

    // event ue_auto_height de uo_entete
    il_hauteur_entete = al_height
    of_relayout()          // reposiciona el contenido situado debajo

Publicada por: `picture` y `statictext`.

Las bandas no publican esta propiedad — su altura es intrínseca. `ribbon` y `toolbar` no se desplazan verticalmente: una altura fija solo puede producir un hueco vacío bajo la banda o contenido truncado (cinta de opciones replegada, barra de herramientas repartida en dos filas…). Por eso se ajustan siempre, sin nada que activar, y publican de todos modos `ue_auto_height` para que usted recoloque lo que se encuentra debajo.

### Anchura automática — `ib_auto_width` #

Mismo principio para la anchura. La publica únicamente `listbar`, el único componente cuya anchura natural tiene sentido.

Una listbar plegada en riel se estrecha por sí misma y devuelve la anchura al desplegarse: `ib_auto_width` solo le sirve si quiere seguir también la anchura desplegada (la barra se ajusta entonces a la etiqueta más larga).

### Eventos de ratón ambientales — `ib_track_mouse` #

Los eventos de ratón de alta frecuencia están cortados en origen: sin suscripción, el componente no los emite (nada atraviesa el puente hacia PowerBuilder).

    uo_bouton.ib_track_mouse = true    // activa ue_mouse_enter / ue_mouse_leave / ue_rclicked

Publicados por: `button`, `picture`, `statictext`.

Los eventos discretos (clic, selección, menú, soltar…) se emiten siempre, sin suscripción.

## 3.5 Los atajos de teclado #

Un atajo activa un componente esté donde esté el foco dentro de la ventana: el usuario no tiene que volver al botón para accionarlo. Todo componente visual los acepta, sin nada que habilitar.

    uo_enregistrer.of_register_shortcut("Ctrl+S")
    uo_actualiser.of_register_shortcut("F5")

### Escribir una combinación de teclas #

La combinación es una cadena libre, normalizada por la biblioteca: las mayúsculas, los espacios y el orden de los modificadores no tienen ninguna importancia. `"Ctrl+Shift+S"`, `"ctrl + shift + s"` y `"SHIFT+CTRL+S"` designan el mismo atajo, así que es imposible registrar dos variantes por descuido.

Elemento | Formas aceptadas
--- | ---
Modificadores | `Ctrl` (o `Control`), `Alt`, `Shift` — combinables, en cualquier orden
Tecla | una letra `A`–`Z`, una cifra `0`–`9`, `F1` … `F24`, `Enter` (o `Return`), `Escape` (o `Esc`), `Delete` (o `Del`), `Insert`, `Home`, `End`, `PageUp`, `PageDown`

Esta lista es exhaustiva: una tecla que no figure en ella (Tab, Espacio, una tecla del teclado numérico, un signo de puntuación) no activa ningún atajo.

Una tecla sola es una combinación válida (`"F5"`). Una cadena vacía retira el atajo del componente.

> `"Enter"` y `"Escape"` solas no se registran como atajos: ambas teclas quedan reservadas al botón predeterminado y al botón de cancelación (`ib_default` / `ib_cancel` del button). Combinadas con un modificador, vuelven a ser combinaciones corrientes (`"Ctrl+Enter"`).

### Quién gana en caso de conflicto #

Dos componentes pueden pedir la misma combinación, algo frecuente cuando una ventana aloja varias zonas que tienen cada una su « Guardar ». El arbitraje se resuelve en este orden:

1. el componente que tiene el foco del teclado se impone a todos los demás: el atajo de una zona activa nunca queda eclipsado por un vecino;
2. en su defecto, gana el primero registrado.

Solo compiten los componentes visibles y activos de la ventana en primer plano. Volver a registrar una combinación en un componente que ya tenía una la sustituye sin cambiar su rango: reconfigurar una ventana no altera las prioridades.

### Atajos de items #

La sobrecarga de dos argumentos vincula la combinación a un item del componente en lugar de al componente entero; el segundo argumento es el identificador del item:

    uo_barre.of_register_shortcut(/*combinacion*/ "Ctrl+N", /*item*/ "nouveau")
    uo_barre.of_register_shortcut(/*combinacion*/ "Ctrl+P", /*item*/ "imprimer")

### Retirar los atajos #

    uo_barre.of_clear_shortcuts()      // componente E items

`of_reset()` y la destrucción del componente llaman a `of_clear_shortcuts()` por usted: un componente desaparecido nunca conserva una combinación reservada.

### La tecla Alt #

Alt sola no se captura: da el foco a la cinta, que muestra sus keytips (véase ribbon). Por tanto, la biblioteca no intercepta las pulsaciones siguientes: es la cinta quien las lee, como si el usuario la hubiera pulsado con el ratón. Esc o un segundo Alt devuelven el foco al control abandonado. Si ninguna cinta de la ventana declara keytips, Alt conserva su comportamiento habitual de Windows.

Miembro | Efecto
--- | ---
`of_register_shortcut (string as_chord)` | Declara un atajo para el componente; una cadena vacía lo retira
`of_register_shortcut (string as_chord, string as_key)` | Declara un atajo para un item, designado por su identificador
`of_clear_shortcuts ( )` | Retira todos los atajos del componente, items incluidos

## 3.6 Poner un componente a cero: `of_reset()` #

`of_reset()` devuelve el componente a su estado inicial, como si acabara de cargarse:

- el contenido se vacía (items, páginas, paneles…);
- cada propiedad vuelve a su valor predeterminado (formato, colores, modo, etiquetas);
- las anulaciones de estilo y los tooltips establecidos en la instancia se cancelan;
- la caché de propiedades del lado de PowerBuilder se vacía (las relecturas parten de los valores predeterminados);
- el estado nativo también se restablece (menú contextual, modo de visualización…).

    uo_grid.of_reset()          // partir de una cuadricula vacia
    // ... y luego reconstruir

> ⚠️ Reutilizar una instancia para mostrar otra cosa sin llamar a `of_reset()` conserva el estado anterior (un color, un modo, una altura automática). Es la causa más frecuente de un « resto de visualización » inexplicable.

## 3.7 Diagnóstico #

Miembro | Efecto
--- | ---
`of_is_created ( )` → boolean | El componente nativo existe (runtime presente, host válido)
`of_is_ready ( )` → boolean | El contenido web está cargado (`ue_ready` ya activado)
`of_get_last_error ( )` → string | Último mensaje de error detallado de la DLL, tras un valor devuelto `< 0`

Códigos devueltos por los métodos `of_*`:

Valor devuelto | Significado
--- | ---
`≥ 0` | OK (aplicado o puesto en cola)
`-2` | Componente no creado (runtime ausente, host no válido)
`-4` | Operación fallida (captura, escritura de archivo…)
`-5` | Argumento no válido (identificador vacío, valor fuera de los límites)
`-6` | Runtime WebView2 demasiado antiguo para la función solicitada (impresión)

## 3.8 Exportar la representación como imagen #

Todo componente sabe exportarse como imagen, tal como se muestra:

    uo_pivot.of_save_as_png("C:\temp\tableau.png")
    uo_pivot.of_save_as_jpg("C:\temp\tableau.jpg")

Útil para un informe, un archivo adjunto de correo electrónico o el rastro de una incidencia. El componente debe estar creado y su contenido cargado.

## 3.9 Ciclo de vida #

1. Construcción: la webview se crea ya en la construcción del userobject — imprescindible para el alojamiento (pestañas, paneles acoplables): una webview creada después de reasignar el elemento padre de su HWND no se muestra.
2. Cola de espera: sus comandos se ponen en cola mientras `ue_ready` no se haya activado.
3. Listo: `ue_ready`; la cola se reproduce en orden.
4. Redimensionamiento: automático, el componente sigue el tamaño del userobject.
5. Destrucción: al cerrar la ventana; la webview se libera, sin ningún proceso huérfano.

Llame a `PBT_Warmup()` una vez al arrancar la aplicación para que este ciclo resulte imperceptible (Instalación).

## 3.10 Buenas prácticas #

- Establezca el tema predeterminado y el idioma en el objeto aplicación, antes de abrir la primera ventana: así los componentes no presentan ningún parpadeo de estilo.
- Encuadre toda construcción voluminosa entre `of_set_redraw(false)` y `of_set_redraw(true)`.
- Llame a `of_reset()` antes de reutilizar una instancia para otro contenido.
- No bloquee el hilo de la interfaz con un bucle PowerScript largo entre la creación y la visualización: la inicialización de la webview necesita el bucle de mensajes (véase FAQ).

← Primeros pasos · Índice · Temas →

Para imprimirlo en lugar de exportarlo, véase Imprimir.