11. Migrar de la 3.0 a la 4.0 #
← Migrar de la 2.0 a la 3.0 · Índice · Notas de la versión →
La 4.0 retira dos métodos de los treinta y dos componentes de la 3.0 — of_set_data de la tabla cruzada, renombrado of_from_datastore (11.33), y of_transform de n_pbt_xml, que se va con XSLT (11.31) — y cambia firmas que el compilador le muestra línea a línea: tres propiedades renombradas (id_minimum e id_maximum de la barra de progreso, 11.6; il_doc_line del editor de código, 11.28), argumentos de evento añadidos o renombrados (ue_tile_moved, 11.6; ue_failed del cliente REST, 11.6, y del vídeo, 11.26; ue_error de la imagen, 11.25; ue_cell_double_clicked, 11.33; ue_closed de la paleta de comandos, 11.36; ue_toast_clicked, ue_toast_action y ue_toast_dismissed del toaster, 11.38), of_count y of_keys_at en long (11.34) y métodos que ahora devuelven un código (of_refresh, of_select, of_show, of_scroll_to_end, of_set_header…). La 11.6 alinea además dos recuentos, un evento y la ruta del árbol JSON con el resto de la biblioteca: es ahí, con las secciones citadas, donde hay que retocar código que compilaba. Lo que cambia se concentra en la cinta (ribbon), que ha sido revisada por completo. Dos cambios piden releer su código: la pregunta planteada antes de un mosaico de galería (11.1) y los handles liberados por una eliminación (11.2). Los demás corrigen comportamientos que quizá haya sorteado (11.3) — entre ellos el clic derecho en un elemento, que ya solo produce un evento en todos los componentes, y la ruta de navegación (breadcrumb), cuyas direcciones y rechazos se han revisado. Vienen después el generador n_pbt_menu2ribbon (11.4) y lo que se ha añadido (11.5). La barra lateral (listbar) sigue también la regla del 11.1: su pregunta antes de una selección es ahora opcional. Las pestañas (tab) siguen la misma regla: sus dos preguntas, antes de un cambio de pestaña y antes de un cierre, son ahora opcionales (11.1). Por último, los temas siguen a la aplicación (11.7): el acento de la aplicación sobrevive a un cambio de tema, y un eje de tema dejado vacío sigue el de la aplicación. La revisión de seguridad de crypto (11.8) cierra una confusión de algoritmo de los tokens JWT, sustituye varios silencios por un rechazo que dice su motivo y fija la regla de las rutas relativas de los archivos.
La revisión completa del 28/09/2026 releyó cada componente entregado; lo que cambia tiene una sección por componente: restclient (11.9), speechout (11.10), statictext y el texto enriquecido (11.11), breadcrumb (11.12), buttonbar y el teclado de ventana (11.13), button (11.14), menubar (11.15), ribbon (11.16), toolbar (11.17), statusbar (11.18), dockcontainer (11.19), tab (11.20), listbar (11.21), tilesbox (11.22), stepbar (11.23), progressbar (11.24), picture (11.25), video (11.26), pdfviewer (11.27), codeeditor (11.28), jsontree y xmltree (11.29, 11.30), xml (11.31), json (11.32), crosstab (11.33), shellexplorer (11.35), commandpalette (11.36), radialmenu (11.37), toaster (11.38) y messagebox (11.39); la base común tiene dos secciones, 11.34 y 11.40. Una regla las atraviesa, la de PowerBuilder (SelectTab lanza SelectionChanged): un cambio pedido por su código lanza el mismo evento que un gesto del usuario, y plantea la pregunta del veto si la ha activado; el método devuelve -4 si usted la rechaza. Vale para una selección (of_select_*), una apertura o un plegado (of_expand, of_collapse, of_open), un zoom, una pantalla completa, un capítulo; no se lanza nada cuando nada cambia, y of_reset sigue en silencio. Asignar un VALOR (una marca, un texto, un volumen, un filtro) sigue en silencio, como SetItem. ue_layout_changed se lanza en ambos casos: dice que ha cambiado una disposición que guardar.
11.1 Las preguntas antes de un cambio son ahora opcionales #
En la 3.0 la cinta planteaba de oficio la pregunta ue_gallery_selection_changing antes de retener un mosaico: ib_veto_gallery valía true. En la 4.0 vale false, como todo evento cancelable de la biblioteca: cada pregunta cuesta una ida y vuelta hasta PowerBuilder, y solo una aplicación que responde debe pagarla. Una aplicación que no programaba el evento no ve ninguna diferencia. Novedad: of_select_gallery_item lanza ue_gallery_selection_changed como un clic y, con la pregunta activada, también la plantea; un rechazo le hace devolver -4, el mosaico no cambia.
// 4.0 : the question is only asked on request. If your ue_gallery_selection_changing
// returns false for some tiles, switch it on where the ribbon is built.
uo_ribbon.ib_veto_gallery = true
La receta: busque ue_gallery_selection_changing en sus descendientes de la cinta; donde esté programado, añada ib_veto_gallery = true a la construcción. Donde escribía ib_veto_gallery = false para desactivar la pregunta, la línea ya no sirve y puede quitarse.
La barra lateral (listbar): ib_veto_selection #
Misma regla: en la 3.0 la listbar planteaba de oficio ue_selection_changing antes de cada cambio de selección (ib_veto_selection valía true). En la 4.0 vale false. Si programaba ue_selection_changing para rechazar un cambio, ponga ib_veto_selection = true: sin ello, el evento ya no se dispara y la selección pasa sin preguntarle. Activada, la pregunta se plantea también para una selección pedida por su código (of_select_item, of_clear_selection), como en la 3.0; un rechazo les hace devolver ahora -4, y la selección no se mueve. ue_selection_changed se dispara como antes.
// 4.0 : the listbar only asks on request. If your ue_selection_changing
// returns false (unsaved changes), switch the question on where the bar is built.
uo_nav.ib_veto_selection = true
La receta: busque ue_selection_changing en sus descendientes de u_pbt_listbar; donde devuelva false, añada ib_veto_selection = true a la construcción. Donde escribía ib_veto_selection = false para desactivar la pregunta, la línea ya no sirve y puede quitarse.
Las pestañas (tab): ib_veto_selection e ib_veto_close #
Misma regla, dos preguntas: en la 3.0 el componente tab planteaba de oficio ue_selection_changing antes de cada cambio de pestaña y ue_tab_closing antes de cada cierre (ib_veto_selection e ib_veto_close valían true). En la 4.0 ambos valen false. Si programaba ue_selection_changing o ue_tab_closing para rechazar, ponga ib_veto_selection = true o ib_veto_close = true: sin ello, el evento ya no se dispara y la pestaña cambia o se cierra sin preguntarle. Activada, la pregunta se plantea también para of_select_page, como en la 3.0, pero antes de que la pestaña cambie: un rechazo le hace devolver -4 y nada se mueve; una aceptación cambia la pestaña de inmediato, ue_selection_changed se dispara como antes, y of_selected_key() leído justo después ya devuelve la nueva clave.
// 4.0 : the tab only asks on request. If your ue_selection_changing or
// ue_tab_closing returns false (unsaved changes), switch the questions on.
uo_tabs.ib_veto_selection = true
uo_tabs.ib_veto_close = true
La receta: busque ue_selection_changing y ue_tab_closing en sus descendientes de u_pbt_tab; donde uno devuelva false, añada la línea correspondiente a la construcción. Donde escribía = false para desactivar una pregunta, la línea ya no sirve y puede eliminarse.
11.2 Las adiciones dicen lo que rechazan, las eliminaciones liberan sus handles #
En la 3.0, una adición que la cinta ignoraba — una clave ya ocupada, una entrada de menú bajo un simple botón — devolvía igualmente 0, y no aparecía nada. En la 4.0 devuelve -5, y el código del envío (-2 si el componente no está creado) en lugar de un 0 fijo. Y una eliminación libera los handles de lo que se va: una variable que guardaba el handle de un control eliminado deja de ser válida.
| Qué | 3.0 | 4.0 |
|---|---|---|
| Añadir una clave ya ocupada (pestaña, grupo, control) | 0, ignorada | -5 |
| Entrada de menú, opción de combo o mosaico bajo un control de otra naturaleza | 0, ignorada | -5 |
of_add_tab, of_insert_tab, of_add_group, of_add_contextual_*, of_add_qat sobre un componente no creado | 0 | -2 |
Un handle guardado tras of_remove_tab, of_remove_group, of_remove_item u of_clear | seguía en memoria hasta la destrucción de la cinta | liberado: IsValid() devuelve false |
// 4.0 : after a removal or of_clear, ask the component for the handle again
uo_ribbon.of_clear()
uo_ribbon.of_add_tab(/*key*/ "home", /*title*/ "Home")
uo_ribbon.of_add_group(/*keys*/ "home/clip", /*title*/ "Clipboard")
uo_ribbon.of_add_button(/*keys*/ "home/clip/paste", /*label*/ "Paste", /*image*/ "")
uo_ribbon.of_item(/*keys*/ "home/clip/paste").ib_enabled = false
La receta: no guarde un handle más allá de una eliminación o de un of_clear — vuelva a pedirlo con of_tab, of_group, of_item u of_menu_item, lo que no cuesta nada. Donde un error de construcción le importe, compruebe el código que devuelve la adición.
11.3 Otros comportamientos que puede notar #
Cuando la ventana es estrecha, la cinta pliega sus grupos en un botón que abre un panel. De ese panel vienen las diferencias más visibles:
| Qué | 3.0 | 4.0 | |
|---|---|---|---|
| Botón, o parte principal de un split, pulsado en el panel de un grupo plegado | ue_menu_selected("", false) | ue_clicked(dirección) | |
| Entrada de menú elegida en ese panel | dirección incompleta | dirección completa, cascada incluida | |
| Control atenuado, o entrada oculta, en ese panel | respondía / se mostraba | no responde / no se muestra | |
| Split grande reducido por falta de espacio | un clic solo abría el menú | dos partes: el comando y la flecha del menú | |
Entrada de menú con la clave de su control (home/clip/paste/paste) | ue_clicked("home/clip/paste") | ue_menu_selected("home/clip/paste/paste", …) | |
of_menu_item("…/exp/pdf") cuando una cascada también contiene pdf | podía apuntar a …/exp/more/pdf | apunta a la entrada que nombra la dirección | |
as_from_keys de ue_gallery_selection_changed sin mosaico anterior | la dirección de la galería | "" | |
Releer un handle de la barra de acceso rápido, il_color de un selector de color, ii_visible_items, is_keytip de una pestaña, il_color de un grupo contextual, of_count() de una galería | vacío, -1 o 0 | el valor en pantalla | |
of_keys_at de un combo | "home/font/cb/cb:Arial" | "home/font/cb/Arial": la clave de una opción es su etiqueta | |
of_ctx_group(k).il_color cuando una pestaña también lleva la clave k | coloreaba la pestaña | colorea solo el banner | |
Colores de elemento (il_back_color…) e is_tooltip de una pestaña o de un grupo | sin efecto | aplicados; la información sobre herramientas de un grupo aparece en su título | |
of_remove_item("save") sobre un botón de la barra de acceso rápido | -5 | el botón se retira | |
of_add_app_menu_separator("saveas/s1") | una línea en la raíz del menú | la línea en el submenú de saveas; una clave sin / no cambia | |
of_clear() mientras una pestaña estaba activa | ningún evento | ue_selection_changed("") | |
Spinner con paso decimal (0.1) | 0.30000000000000004 | 0.3 | |
| Atajo Alt (keytip) sobre un control atenuado, cinta minimizada | la cinta seguía abierta | se cierra, no se ejecuta nada | |
| toolbar: combo, spinner o galería atenuados en el panel del chevron | respondían | no responden | |
Todos: clic derecho en un elemento (ue_item_rclicked, ue_panel_rclicked, ue_row_rclicked…) | la página enviaba también el evento del componente, que ningún componente con elementos transmitía a PowerBuilder | un solo evento, el del elemento; un ue_rclicked de componente, donde existe, solo se lanza fuera de los elementos | |
Todos: soltar archivos (ib_allow_drop) | ue_drag_leave y luego ue_drop_files; rutas cortadas a 260 caracteres | solo ue_drop_files, rutas completas; of_reset quita el destino de soltar | |
breadcrumb: of_path() con un segmento oculto, o en la versión de demostración | las claves mostradas: el oculto omitido, los primeros segmentos cortados | la dirección completa del lugar actual: of_truncate(of_path()) acierta; el campo de escritura parte de la misma | |
breadcrumb: of_keys_at(n); of_has("computer/C:") | la clave simple; buscaba entre las ramas del menú | la dirección del segmento; busca entre los segmentos | |
breadcrumb: leer un handle de dirección profunda, of_item("a/b/c").is_text | leía solo el último nivel: vacío en una clave repetida | el segmento de esa dirección | |
breadcrumb: of_truncate, of_remove_item, of_clear_children, of_remove_child sobre una dirección desconocida o una clave simple repetida; of_add_child bajo un segmento ausente o sobre una rama ya ocupada; of_add_item de una dirección que acabaría en otro sitio | 0, ignorado (o solo la última clave retenida) | -5 | |
breadcrumb: un handle guardado tras of_clear, of_remove_item, of_truncate, of_clear_children u of_remove_child | seguía en memoria | liberado: IsValid() devuelve false | |
| breadcrumb: campo de escritura abandonado sin cambios, o ventana cambiada (Alt+Tab) mientras se escribe | ue_path_entered lanzado | nada; lo escrito se mantiene al cambiar de ventana | |
breadcrumb: segmento atenuado; Intro sobre un segmento | su chevron se abría y recibía un soltar; Intro pulsaba también el botón predeterminado de la ventana | totalmente inerte; Intro solo pulsa el segmento | |
buttonbar: Intro mientras otro botón de la barra tiene el foco del teclado | activaba el botón predeterminado (is_default) | activa el botón que tiene el foco, como Windows; el predeterminado se activa cuando ningún botón de la barra tiene el foco | |
buttonbar: botón predeterminado o cancelar atenuado u oculto; is_shortcut de un botón atenuado u oculto | Intro, Esc o Alt+letra tragados sin efecto, incluso en un control PowerBuilder de la ventana | la tecla se queda en la ventana | |
buttonbar: is_shortcut de varias letras ("Save") | Alt+S solo funcionaba si la barra tenía el foco | Alt+S funciona desde cualquier lugar de la ventana (la primera letra, como está documentado) | |
buttonbar: of_add_button / of_insert_button con una clave ya usada o que contiene / o una barra vertical; of_remove_button / of_move_button con una clave desconocida | 0, ignorado | -5 | |
buttonbar: of_remove_button, of_clear_buttons | el atajo Alt+letra, los colores y el tooltip del botón se quedaban (un botón recreado con la misma clave los recuperaba); el handle seguía en memoria | se van con el botón; el handle se libera: IsValid() devuelve false | |
codeeditor: releer is_text de un texto en CRLF | en LF (en CRLF solo si el editor estaba plegado): is_text = ls_original era falso | con los finales de línea recibidos, plegado o no | |
codeeditor: releer is_syntax tras SYNTAX_CSHARP, SYNTAX_CPP, SYNTAX_JAVA, SYNTAX_XML | la gramática usada ("c", "html") | el valor escrito | |
codeeditor: of_insert_text en solo lectura o plegado | insertado en solo lectura; rechazado plegado, pero 0 | -4, no se inserta nada | |
codeeditor: ii_doc_line | solo una banda, a menudo fuera de la vista, ausente con el ajuste de línea; un integer | la línea llega al centro de la vista (una región plegada que la oculta se abre), banda también con ajuste de línea; cursor y selección intactos; un long — renombrado il_doc_line en la 4.0 (sección 11.28) | |
codeeditor: #regionopen con ib_folding | reescrito #region en el texto (is_text, y tras el plegado) | nunca se toca; solo la vista plegada muestra #region | |
codeeditor: ue_find_result | dos veces por of_find, en cada pulsación y (0, 0) al cerrar la barra; lanzado incluso con ib_search_enabled = false | una vez por búsqueda o paso; al escribir si cambia el recuento; nunca al cerrar; of_find sin efecto con la búsqueda desactivada | |
codeeditor: ue_caret_changed | mudo al escribir; plegado, la línea MOSTRADA | lanzado también al escribir; plegado, la línea del margen | |
codeeditor: Ctrl+Z tras Intro o Tab; Intro / Tab en modo plegado | el historial de deshacer se vaciaba; plegado, la tecla modificaba la vista y luego se perdía | cada pulsación se deshace; plegado, la tecla no tiene efecto | |
codeeditor: ib_folding = true con ib_line_numbers = false | las regiones seguían cerradas, sin marcador para abrirlas | un margen estrecho conserva los marcadores | |
commandpalette: of_add_command con una clave ya usada o que contiene / o una barra vertical; of_remove_command con una clave desconocida | 0 (una clave usada daba dos comandos gemelos) | -5 | |
commandpalette: of_open / of_register_shortcut sin ipo_owner ni ipo_receiver | 0, y ningún evento llegaba nunca | -5 | |
| commandpalette: el atajo pulsado mientras la paleta está abierta | sin efecto | cierra la paleta | |
| commandpalette: un objeto paleta destruido (página o ventana cerrada) | su atajo seguía registrado y se tragaba la tecla en la ventana, sin ningún evento | el atajo se va con el objeto, y una paleta que había abierto se cierra | |
commandpalette: atajo mostrado en una fila, en un teclado no latino, con una cifra y Mayús, Ctrl+Space, Ctrl+Up, Ctrl+Del, o una tecla F sola | no respondía; Win + la letra lanzaba el comando cuyo atajo era esa sola letra | responde: la tecla física, leída como la lee la DLL; Win nunca es un modificador | |
commandpalette: un comando cuyo atajo es Ctrl+C, Ctrl+V, Ctrl+Z o Ctrl+A, al escribir | se lanzaba el comando en lugar de copiar, pegar… | el cuadro de búsqueda conserva estas cuatro teclas | |
commandpalette: of_reset | conservaba is_recent, una paleta abierta y el atajo ya registrado | los vacía, la cierra y devuelve el atajo registrado a SHORTCUT_DEFAULT | |
commandpalette: pbm_custom02 de la ventana ipo_owner | disparado en cada evento de la paleta | nunca: solo un ipo_receiver definido lo sigue recibiendo | |
Todos: releer un valor de texto largo (is_text de un script grande) | cada vez más lento con el tamaño (varios segundos); cortado sin aviso más allá de un millón de caracteres | proporcional al tamaño; entero | |
n_pbt_utils.of_dir_has_subfolders | contaba las carpetas ocultas, de sistema y las uniones; ilegible más allá de 260 caracteres | las ignora, como el Explorador (la sobrecarga ab_include_hidden las cuenta); lee rutas largas | |
crosstab: separador de miles de of_set_value_format | valía para TODAS las medidas; "," no reconocido ($1 234 en francés) | ajuste de ESTA medida; ",", ".", " " reconocidos; "" = predeterminado del menú Opciones | |
crosstab: of_set_member_filter(campo, "") | ningún miembro conservado: tabla vacía | quita el filtro, como of_set_member_order("") | |
crosstab: is_show, is_conditional_formatting, of_set_value_format antes de of_add_value_field | perdidos sin aviso | conservados, releídos tal cual, aplicados cuando el campo entra en Valores | |
| crosstab: ordenar una columna o plegar un grupo con el ratón | ningún evento | ue_layout_changed, como todo lo que of_get_layout guarda | |
crosstab: fechas de un DataStore en un equipo no francés (3/15/2025, 15.03.2025) | agrupadas en un mes erróneo o vacío; fechas ordenadas solo por el año | el mes correcto; la columna releída como aaaa-mm-dd, ordenada en el tiempo | |
| crosstab: números mostrados en alemán, italiano, español, portugués | a la americana (1,234.5) | en el idioma de visualización (1.234,5 en alemán) | |
crosstab: of_export_csv | números con punto (1436.5), leídos como texto por un Excel francés; una etiqueta =… se ejecutaba al abrir | separador decimal del idioma de visualización; un apóstrofo delante de = + - @ | |
crosstab: of_export_xlsx | totales y subtotales ocultos exportados igualmente; total arriba ignorado; símbolo « delante » pasado detrás | la tabla tal como en pantalla | |
| crosstab: « Añadir a los valores » del menú de un campo ya en Valores | sustituía sus valores | añade un valor, como el arrastre | |
| crosstab: « Quitar » del menú de un campo filtrado | el filtro seguía activo, invisible | el filtro se va con el campo | |
crosstab: AGG_DISTINCT_COUNT en una columna con celdas vacías | el vacío contaba como un valor | ignorado, como en Excel | |
| crosstab: fórmula que cita una medida calculada | aceptada, valía 0 | rechazada mediante ue_calc_field_error; sus mensajes están en inglés | |
crosstab: ue_layout_changed con una llave en un miembro; ad_value de ue_cell_double_clicked | disposición truncada; valor 0 si un campo en filas se llama value, exponente perdido | leídas enteras | |
crosstab: ib_enabled = false, con el teclado | Intro abría el menú de un chip, Ctrl+C copiaba | mudo | |
| crosstab: teclado | el foco volvía arriba de la página tras cada acción; ninguna forma de mover un campo | el foco se queda en el elemento accionado; el menú de un chip lo mueve (otra zona, izquierda, derecha) | |
| crosstab: tema oscuro | negativos y flechas de iconos poco legibles; celda de escala de colores seleccionada ilegible | una paleta propia en modo oscuro | |
crypto: of_jwt_sign con JWT_EDDSA | la cabecera decía "alg":"EDDSA", que las demás bibliotecas (jose, PyJWT, jjwt) rechazan | "alg":"EdDSA", el nombre registrado; un token 3.0 sigue verificándose | |
crypto: of_generate_keypair de un tipo o tamaño desconocido ("ec_p256", 1024) | 0 y un par RSA 2048, sin decir nada | -5, ambos PEM vacíos, is_last_error nombra las KEY_* admitidas | |
crypto: of_hash_file("SHA-256", …); algoritmo desconocido | vacío, e is_last_error decía « archivo ilegible » de un archivo perfecto | el nombre se lee como lo lee of_hash (SHA-256 = HASH_SHA256); un algoritmo desconocido se dice como tal, aparte de un archivo ilegible | |
crypto: of_base64_encode_file de un archivo demasiado grande (más de 64 MB en 32 bits, 512 MB en 64 bits); de un archivo ausente en demo | « archivo ilegible »; « límite de la demo » | « demasiado grande para este proceso »; « archivo ilegible » — is_last_error da la razón real | |
crypto: is_last_error tras una llamada nativa correcta (of_crc32, of_hash en MD5, of_hash_file…) | conservaba el error de la llamada anterior | vacío | |
crypto: clave HMAC vacía; exp/nbf no numérico de un JWT; of_password_verify de un valor de más de 10 000 000 rondas; of_random_password de un juego desconocido | error oscuro del motor; token válido para siempre; minutos de cálculo; CHARSET_ALL tomado sin decir nada | rechazados de inmediato, is_last_error dice por qué | |
crypto: of_base64_decode_to_file("", ruta) | -5 | 0, un archivo vacío | |
dockcontainer: of_add_panel con un as_relative_to desconocido, un control ya alojado, una clave con / o una barra vertical, o POSITION_STACK sobre el panel principal | 0: el panel se acoplaba a la raíz, el control se alojaba dos veces, o el panel apilado sobre el principal quedaba invisible | -5, y el control no se toca | |
dockcontainer: of_move_panel hacia un destino desconocido, o POSITION_STACK sobre el panel principal | 0: el panel iba contra la raíz, o nada se movía | -5 | |
dockcontainer: of_remove_panel, of_reset | el control volvía a su ventana oculto, en la posición y con el tamaño del dock | vuelve a su sitio, con su tamaño y con la visibilidad que tenía antes de of_add_panel | |
dockcontainer: is_main asignado antes de of_add_panel; is_main = ""; is_main de un panel apilado | ignorado en silencio; no se transmitía nada; sus vecinos de pila se volvían invisibles | se conserva y se aplica al añadir el panel; se quita el panel principal; sale de su pila, los demás siguen visibles | |
dockcontainer: of_set_layout de una disposición que no conoce ninguno de los paneles, o de un texto que no es una disposición | el dock se vaciaba, o se rechazaba el mensaje entero; 0 | los paneles permanecen, colocados junto a los demás (nunca sobre el principal); -5 para un texto que no es una disposición | |
dockcontainer: ue_layout_changed | lanzado solo al arrastrar, redimensionar y acoplar: una disposición guardada en este evento olvidaba un panel cerrado o la pestaña activa | también se lanza cuando un panel se cierra, se oculta, se selecciona o se renombra, y cuando cambia el panel principal | |
| dockcontainer: clic en la pestaña (o encabezado) del panel ya activo | ue_panel_selected en cada clic | nada: el evento indica un CAMBIO de panel activo | |
dockcontainer: panel desfijado (ib_pinned = false) | su lengüeta solo se abría con un clic (la doc decía « al pasar el cursor ») | se abre al pasar el cursor (400 ms) o con un clic; abierta al pasar el cursor, se repliega cuando el ratón la abandona | |
| dockcontainer: control PB estándar (MultiLineEdit, botón…) en un panel flotante | sus notificaciones (modified, clicked, colores) ya no llegaban | llegan como cuando está acoplado | |
dockcontainer: ocultar un panel flotante (ib_visible = false) | su ventana seguía abierta | se cierra; al volver a mostrarse, el panel vuelve acoplado a su sitio | |
| dockcontainer: ventana flotante | su zona útil era más pequeña que el panel (barra de título descontada), podía salirse de la pantalla, y un título con etiquetas las mostraba | la zona útil tiene el tamaño del panel, la ventana se queda en la pantalla, el título es texto | |
| dockcontainer: divisor con el teclado | la primera flecha lo hacía saltar al 50 % | parte de su posición mostrada | |
json: valor nulo dado a of_set_number, of_add_number, of_set_boolean, of_add_boolean, o valor nulo o vacío dado a of_set_raw / of_add_raw | todo el documento se volvía nulo (of_text devolvía null) o dejaba de ser JSON; un booleano nulo se escribía false | se escribe null, el resto del documento queda intacto | |
json: escribir en un documento CARGADO cuya clave se escribe "k" : v o con un carácter escapado, seguido de un salto de línea, o cuya raíz es un array; of_set_members sobre tal documento | la clave se añadía dos veces (la relectura devolvía el valor antiguo), la escritura devolvía -4 sin razón, o el documento se sustituía por el fragmento | el valor se sustituye o se añade como en un documento construido; of_set_members devuelve -4 si la raíz no es un objeto | |
json: of_set_*("items/1/qty", …) — una clave NUEVA en un objeto situado en un array | -4 | 0: la clave se añade al primer elemento; un array se atraviesa con un índice existente, solo se crea una clave de objeto | |
json: ruta con un segmento vacío (/name, a//b, a/) | se creaban claves llamadas ""; of_remove podía quitar otro valor | -5, no se escribe nada; un lector responde "ausente" | |
json: of_load de un número mal escrito (-, 01, +1, .5, 1., 1e) | 0, y of_is_valid verdadero | -4, como todo JSON no válido | |
json: of_get_number de un número con exponente (1e-7, 2.5E+3) o con más de 9 decimales | exponente ignorado (2.5E+3 leído 2,5), decimales cortados a 9 | exponente resuelto, 17 cifras significativas conservadas | |
| json: leer o modificar un documento grande (una respuesta de API de 80 KB, 2000 filas) | segundos por llamada (20 s para of_count), la aplicación parecía congelada | un tiempo proporcional al tamaño; leer las filas una a una (rows/1, rows/2…) o recorrer las claves con of_name ya no vuelve a empezar desde el principio | |
listbar: of_add_section / of_insert_section con una clave ocupada o que contiene / o una barra vertical; of_add_item / of_insert_item con una dirección ocupada o bajo una sección que la barra no añadió; of_remove_item, of_move_item, of_select_item, of_remove_section, of_move_section con una dirección desconocida | 0, ignorado; una entrada bajo una sección desconocida aparecía bajo la última sección | -5; of_add_section devuelve también -2 cuando el componente no está creado | |
listbar: un handle conservado tras of_remove_item, of_remove_section u of_clear | quedaba en memoria | liberado: IsValid() devuelve false | |
listbar: quitar o vaciar lo que lleva la selección (of_remove_item, of_remove_section, of_clear) | la selección desaparecía sin evento; tras of_clear, of_selected_key devolvía aún la antigua | ue_selection_changed(antigua, "") | |
listbar: ue_layout_changed | mudo al plegar una sección y al quitar una entrada; disparado al entrar en el riel | disparado al plegar (clic o ib_collapsed de una sección) y al quitar; ya no para el riel, que no forma parte de la disposición | |
listbar: riel (ib_collapsed = true) | las entradas de una sección plegada desaparecían; una entrada sin icono mostraba un cuadro vacío; of_reset dejaba la barra estrecha | todas las entradas, un filete entre secciones, la inicial de una entrada sin icono, la etiqueta al pasar el ratón; of_reset devuelve a la barra su ancho | |
listbar: is_image de una sección; un color puesto en la sección k cuando una entrada tiene también la clave k | sin efecto; la entrada tomaba también el color | el icono se dibuja delante del título; solo la sección toma el color | |
| listbar: teclado | tras un cambio de etiqueta o una selección, las flechas dejaban de responder; sin selección la barra quedaba fuera de la tabulación; los títulos de sección solo se plegaban con el ratón | el foco se queda en la entrada; una parada de tabulación; los títulos se alcanzan con las flechas y se pliegan con Intro o Espacio | |
listbar: entrada desactivada (ib_enabled = false); ib_auto_width en una barra desplegada | etiqueta atenuada, poco legible en temas claros; informaba del ancho ya dado | el color « desactivado » del tema; el ancho que pide la línea más larga | |
listbar: arrastrar una entrada (ib_reorderable) | botón soltado fuera de la ventana: la entrada seguía al ratón; el clic final podía seleccionar la entrada soltada | el gesto termina al volver el ratón; el final de un arrastre no selecciona nada | |
menubar: ue_menu_opening | lanzado junto al desplegable: lo que cambiaba (atenuar Pegar…) solo se veía en la apertura siguiente | el desplegable espera a que el evento vuelva: el cambio se ve en esta apertura | |
menubar: of_item, of_remove_item sobre una entrada cuya clave existe también en otra cascada (file/export/pdf, file/print/pdf) | podían alcanzar la otra entrada (la primera encontrada); el atajo mostrado en una disparaba la otra | alcanzan la entrada que nombra la dirección; una clave sola ya no busca en las cascadas | |
menubar: of_add_menu, of_add_item, of_add_separator sobre una clave ocupada, con / o una barra vertical, o bajo un menú o un padre desconocidos; of_remove_item / of_remove_menu de una dirección desconocida | 0; una entrada con el padre mal escrito iba a la raíz del menú, una clave ocupada daba entradas gemelas | -5 | |
menubar: un handle o un atajo tras of_remove_item, of_remove_menu u of_clear | el handle se quedaba en memoria; el atajo seguía armado y se tragaba la tecla, que ya no llegaba a la aplicación | handle liberado: IsValid() devuelve false; atajo desarmado | |
| menubar: entrada atenuada, oculta o quitada mientras su desplegable está abierto; atajo de una entrada bajo una cascada atenuada | el clic o la tecla aún lanzaban ue_item_selected | nada | |
menubar: colores de ítem (il_back_color…) de un menú o de una entrada; is_tooltip de un menú | sin efecto; un color se releía -1 | aplicados: en el título del menú, en la entrada del desplegable; la información de un menú se muestra en su título, una entrada del desplegable no muestra ninguna | |
| menubar: teclado | las flechas se quedaban bloqueadas en un menú atenuado; en RTL, izquierda y derecha invertidas; Alt+letra de un menú oculto tapaba la misma letra de otro menú; tras Alt+letra y una elección, el Alt siguiente apagaba | los menús atenuados se saltan; las flechas siguen el sentido de escritura; solo un menú visible responde a su letra; el Alt siguiente vuelve a encender la barra | |
menubar: una cascada cuyas entradas están todas ocultas; of_count("file"), of_keys_at | se elegía como un comando (file/export); los separadores se contaban (__sep0) | atenuada; solo se cuentan las entradas | |
menubar: of_item("file") — una dirección de un nivel | releía el menú, y escribía en una entrada «file» inexistente | no designa nada: use of_menu("file") | |
Todo texto leído de un componente o de un JSON: escapes \b y \f | quedaban escritos tal cual | decodificados (retroceso, salto de página) | |
| messagebox: un cuadro que no se pudo abrir (runtime de WebView2 ausente, motor no listo) | of_show y los atajos (of_info…) devolvían 0, como un cierre: of_info hacía creer que se había mostrado un cuadro | -4 (-6 si falta el runtime), el motivo con of_get_last_error(); of_choose y of_prompt devuelven "" y of_get_last_error lo dice. Una prueba = 1 para «confirmado» sigue siendo correcta | |
messagebox: Esc o Alt+F4 mientras un botón de cancelación sigue en cuenta atrás (of_add_button_timed) | cerraban el cuadro (0) | no hacen nada hasta el final de la espera: está para hacer leer | |
| messagebox: el texto del mensaje y de la instrucción | no se podía seleccionar; pulsar sobre él movía el cuadro | se selecciona y se copia (Ctrl+C copia la selección, o todo el cuadro sin selección); el cuadro se mueve por su título, su icono, sus márgenes | |
messagebox: una entrada de más de unos 900 caracteres (of_prompt, ib_input) | volvía vacía, como una cancelación | vuelve entera | |
messagebox: al_hwnd = 0, un cuadro abierto desde otro, un cuadro movido y luego agrandado | se desactivaba todo el escritorio; la ventana se reactivaba mientras el primer cuadro seguía esperando; el cuadro volvía al centro | sin propietario no se desactiva nada; la ventana sigue desactivada mientras un cuadro la espera; el cuadro conserva su sitio | |
progressbar: rango vacío o invertido (id_maximum = 0 y luego id_value = 0) | 0 % sea cual sea el valor | 100 % en cuanto el valor alcanza el máximo (un lote vacío está terminado), 0 % antes | |
| progressbar: el porcentaje mostrado, el anillo | redondeado al más cercano (99,5 % mostraba «100%»); anillo fijo de 128 px; en espera, el anillo giraba con el arco del valor (vacío a 0: nada se movía) | redondeado hacia abajo: 100 % significa terminado; el anillo sigue el tamaño del control; en espera gira un arco fijo; si Windows pide menos animaciones, la espera pulsa | |
Cualquier propiedad double releída (id_value…) en un equipo con coma decimal | la parte decimal se leía mal (0,5 no se releía como 0,5) | se relee exactamente, sea cual sea el separador del equipo | |
speechout: un nuevo of_speak (u of_close) durante una lectura | la lectura cortada no levantaba ningún ue_stopped | toda lectura empezada termina con un ue_stopped | |
speechout: il_timeout_ms = 0 | cortaba la lectura síncrona a los 5 s | sin límite (24 horas como máximo) | |
| speechout: una voz que no pudo crearse (runtime WebView2 ausente, página rechazada) | of_speak, of_speak_from, of_pause, of_resume, of_enqueue, of_clear_queue devolvían -1, of_open el código bruto de la DLL | -2 (componente no creado, como en todas partes), is_last_error dice por qué; -4 si la página no respondió | |
speechout: of_speak_sync llamado desde un evento levantado durante otro of_speak_sync | cortaba la lectura de la llamada exterior, que devolvía 0 de todos modos | rechazado: -4, is_last_error = "already speaking synchronously" | |
speechout: of_speak_from más allá de la última frase | 0, no se leía nada | -5 | |
speechout: of_is_speaking() justo después de of_speak | false mientras no había llegado la lista de voces | true desde of_speak | |
speechout: of_pick_voice("", género), o lista de voces ilegible | elegía en el idioma de la página; un fallo vaciaba is_voice | "" = is_lang; un fallo conserva is_voice y rellena is_last_error | |
speechout: of_voice_used, of_duration, of_spoken_text justo después de cambiar is_lang o el diccionario | podían responder con los ajustes anteriores | responden con los ajustes recién puestos | |
| speechout: error de script en la página de la voz | silencioso | ue_error("script error: …") | |
statictext: il_back_color con ii_corner_radius | el fondo llenaba todo el rectángulo, solo el trazo del borde se redondeaba | el fondo sigue el redondeo; fuera de las esquinas, el color del objeto sobre el que está el bloque, que también toma el userobject | |
statictext: is_marquee_direction con lectura de derecha a izquierda | invertido: MARQUEE_START se desplazaba hacia el final, "left" hacia la derecha | MARQUEE_START se desplaza hacia el inicio de la lectura; "left" / "right" siguen siendo físicos | |
statictext: releer il_back_color, ii_marquee_speed, is_marquee_direction, ib_marquee_overflow_only, ib_marquee_pause_on_hover, is_font_family | -1, 0, "", false fuera cual fuera el valor; una familia con espacio entre comillas | el valor asignado | |
statictext: ii_marquee_speed = 0 | mantenía la velocidad anterior | velocidad por defecto (60) | |
statictext: of_reset tras un borde | color y grosor conservados: el borde siguiente los heredaba | borrados | |
statictext: ib_track_mouse, componente desactivado durante el paso del cursor | ue_mouse_leave nunca lanzado | ue_mouse_leave lanzado al salir | |
statictext: desplazamiento (ib_marquee) | un [foldarea] ya no se plegaba después; con ib_marquee_overflow_only, un cambio de ALTURA volvía a empezar; con ib_wrap = false, un ue_text_overflow(true) erróneo | corregido: el texto ya no se copia, solo una ANCHURA realmente nueva se vuelve a medir, y el desplazamiento muestra todo el texto | |
statictext: ib_hover_effect y enlaces con el cursor encima en un tema oscuro | oscurecidos (menos contraste) | aclarados | |
Texto enriquecido: [symbol=clock] | se mostraba tal cual | un glifo monocromo; un nombre desconocido sigue siendo texto | |
statusbar: of_add_panel / of_insert_panel con una clave ocupada o que contiene / o una barra vertical; of_move_panel, of_remove_panel, of_clear_menu, of_add_menu_separator con un panel desconocido; of_add_menu_item / of_insert_menu_item bajo un panel desconocido o con una entrada ocupada; of_remove_menu_item, of_move_menu_item con una dirección desconocida | 0, ignorado | -5 | |
statusbar: of_insert_panel(…, 0) | añadido al final de la barra | el primero, como toda posición (0 o menos = el primero) | |
statusbar: un separador (of_add_sep) en of_count, of_keys_at y las posiciones de of_insert_panel / of_move_panel | contado como un panel de clave vacía | ignorado: una posición cuenta paneles | |
statusbar: un separador entre dos paneles ALIGN_END | dibujado al final del grupo de inicio | entre los dos: abre el grupo del panel que lo sigue | |
| statusbar: clic derecho en un panel no clicable | ningún evento | ue_panel_rclicked (un panel desactivado sigue mudo) | |
statusbar: of_panel(k).ii_progress leído en un panel sin barra | 0, como una barra al 0 % | -1 | |
statusbar: of_menu_item("k"), una dirección de un solo nivel | escribía en el panel k (ib_enabled lo atenuaba) | no designa nada: no escribe en ningún sitio, se relee vacío | |
statusbar: colores e información sobre herramientas de un panel tras of_clear, y luego un panel añadido con la misma clave | retomados por el nuevo panel | olvidados: el panel empieza limpio | |
statusbar: is_state en un tema oscuro; panel desactivado | colores del tema claro, poco legibles; atenuado por transparencia, fondo perdido al pasar el ratón | una paleta oscura legible; atenuado con el color del tema, fondo conservado | |
| statusbar: un panel cuyas entradas de lista están todas ocultas | mano y flecha, clic sin efecto | un panel normal, clicable o no según ib_clickable | |
| statusbar, toolbar: flechas Izquierda / Derecha en lectura de derecha a izquierda (RTL) | al revés: Derecha iba hacia la izquierda de la pantalla | en el sentido de la pantalla, como la pestaña y la cinta | |
stepbar: asignar ii_current | no se lanzaba nada | ue_step_changed cuando la barra se mueve, como prometía la documentación | |
| stepbar: retirar, insertar o desplazar un paso antes del paso en curso | el paso en curso cambiaba sin avisar (era un rango) | el mismo paso sigue en curso; si es él el que se retira, la barra pasa al siguiente alcanzable y lanza ue_step_changed | |
stepbar: ii_current asignado antes de los pasos; of_clear_steps | devuelto al primer paso; el paso en curso sobrevivía al vaciado | se conserva y se aplica en cuanto el paso existe; el vaciado vuelve a empezar en el primer paso | |
stepbar: of_add_step / of_insert_step con una clave ocupada o que contiene / o una barra vertical; of_move_step, of_remove_step con una clave desconocida | 0, ignorado | -5 | |
stepbar: of_insert_step(…, 0) | añadido al final de la barra | insertado al principio, como toda posición de la biblioteca (of_add_step añade al final) | |
| stepbar: flechas izquierda / derecha en un idioma de derecha a izquierda | la flecha derecha iba al paso siguiente, dibujado a la izquierda | invertidas: la flecha izquierda va al paso siguiente | |
tab: of_add_page / of_insert_page con una clave que contiene / o una barra vertical; un envío fallido | 0; la página seguía alojada y su clave ocupada | -5; un envío fallido devuelve su código, la página vuelve a su ventana y la clave queda libre | |
tab: of_select_page de una página oculta, desactivada o más allá del límite de la demo | 0, y la página se mostraba bajo una barra sin pestaña activa | -5, nada se mueve | |
tab: cruz, atajo (is_shortcut), «Cerrar todas / las demás / a la derecha» en una pestaña desactivada u oculta | la pestaña se activaba o se cerraba, y PB liberaba su página | sin efecto: una pestaña que no se puede ver ni alcanzar no se activa ni se cierra | |
| tab: la pestaña activa se cierra, se retira o se oculta | tomaba el relevo la siguiente o la primera, aunque estuviera oculta o desactivada; el as_from_key de ue_selection_changed estaba vacío | toma el relevo la vecina seleccionable (derecha, si no izquierda); as_from_key nombra la pestaña realmente abandonada, aunque haya desaparecido | |
| tab: ninguna pestaña visible (todas ocultas, o una disposición que las oculta) | la página de la antigua pestaña seguía visible, sin evento | la página se oculta y se lanza ue_selection_changed(antigua, ""); volver a mostrar una pestaña la selecciona | |
tab: un handle conservado tras of_remove_page o un cierre; su atajo | se quedaban; una página reabierta con la misma clave recuperaba color, tooltip y antiguo atajo | liberados: IsValid() devuelve false, la página vuelve nueva | |
tab: of_set_layout con un texto que no es JSON | 0, y el mensaje salía roto | -5, no se envía nada | |
| tab: teclado en la barra de pestañas | Intro perdía el foco; las flechas se detenían en las pestañas desactivadas o desbordadas; ni Arriba/Abajo en barra vertical ni inversión en escritura de derecha a izquierda | el foco se queda en la pestaña; las flechas siguen el eje de la barra y saltan lo que no responde; Supr cierra una pestaña que se puede cerrar | |
| tab: texto de una pestaña desactivada; tooltip de una pestaña con el título truncado | atenuado en bloque (ilegible al pasar el ratón en office2007 oscuro); el nombre completo nativo ocultaba su tooltip | texto en el color desactivado del tema, legible; su tooltip tiene prioridad | |
tilesbox: of_get_layout | devolvía siempre "": una disposición guardada nunca se restauraba | la disposición entera, el mismo texto que ue_layout_changed; también conserva el tamaño y la visibilidad de cada mosaico y la visibilidad de cada grupo | |
tilesbox: of_set_layout que nombra un mosaico bajo otro grupo distinto del suyo; un texto que no es JSON | el primer mosaico con esa clave, en cualquier grupo, se movía allí; 0 | un mosaico se encuentra por su grupo y su clave: la entrada se ignora; -5 ante un texto que no es JSON | |
tilesbox: of_add_group con una clave ya ocupada; of_add_tile / of_insert_tile con una clave ocupada o bajo un grupo nunca añadido; una clave vacía, o con / o ` | ` | 0; un grupo añadido de nuevo se renombraba, un mosaico bajo un grupo desconocido lo creaba con su clave | -5, no se añade nada: añada primero el grupo con of_add_group |
tilesbox: of_move_tile, of_remove_tile, of_add_live_item, of_clear_live_items sobre un mosaico inexistente; of_remove_group sobre un grupo inexistente | 0 | -5 | |
tilesbox: un handle conservado tras of_remove_tile, of_remove_group, of_clear, o un mosaico arrastrado a otro grupo | quedaba en memoria; un mosaico recreado en la misma dirección recuperaba colores e información sobre herramientas del antiguo, un mosaico arrastrado los perdía | el handle se libera (vuelva a pedirlo con of_tile / of_group); colores e información sobre herramientas siguen al mosaico arrastrado y se van con lo que se elimina | |
statusbar, stepbar, crosstab: un handle conservado tras of_remove_panel, of_remove_menu_item, of_clear_menu, of_clear (statusbar), of_remove_step, of_clear_steps (stepbar), of_remove_calc_field u of_remove_calc_measure (crosstab) | seguía en memoria hasta la destrucción del componente | liberado: IsValid() devuelve false; vuelva a pedirlo con of_panel, of_menu_item, of_step u of_field | |
Todos los componentes con elementos: releer il_back_color_hover / il_text_color_hover de un elemento nunca coloreado | 0, el negro | -1, «el color del tema», como los otros tres colores del elemento | |
tilesbox: releer ib_enabled / ib_visible de un grupo, o ii_badge_size | false, false, 0 fuera lo que fuera lo puesto | el valor vigente | |
tilesbox: grupo desactivado (ib_enabled = false) | su encabezado aún lo plegaba (clic, Intro) y lanzaba ue_group_toggled; un mosaico arrastrado podía caer en él | inerte: ni pliegue, ni evento, ni soltar | |
tilesbox: colores de item (il_back_color, il_text_color, il_accent…) puestos en un grupo | sin efecto; el color de un grupo a podía pintar un mosaico a de otro grupo | pintan el encabezado del grupo (fondo, título, paso del ratón, filete de acento), nunca sus mosaicos | |
tilesbox: of_tile(""), o una dirección de uno o tres niveles | poner una propiedad detenía la aplicación («Array boundary exceeded») o escribía en el grupo | un handle inerte: no escribe nada y se lee vacío | |
| tilesbox: teclado y lector de pantalla | el foco se perdía en cada actualización venida de PB (un distintivo) y el menú de tamaño se cerraba; menú de tamaño solo con el ratón; en RTL, flechas al revés; etiquetas leídas con sus etiquetas de formato | foco y menú conservados; menú de tamaño con el teclado (flechas, Intro, Esc); flechas en el sentido de lectura; texto sin formato | |
toaster: il_max_visible | predeterminado 5, enviado por CADA toaster: un toaster que había establecido 10 volvía a 5 en cuanto otro mostraba un toast | predeterminado 0 = no se envía nada; el límite es COMÚN a la aplicación y gana el último valor establecido (5 mientras nadie lo establezca) | |
toaster: of_show de un toast que espera sitio | devolvía 0: el toast no se podía cerrar con of_close, y sus eventos llevaban un id nunca recibido; cola llena (200): 0 también, toast perdido sin decir nada | un identificador real, enseguida; of_close lo retira de la cola; cola llena: -4 | |
toaster: dos toasts sin is_key con un botón de la misma clave | el segundo REEMPLAZABA al primero (la clave del botón se tomaba por la del toast) y of_show devolvía el id del primero | dos toasts, dos identificadores; solo is_key actualiza en su sitio | |
| toaster: eventos de varios toasters | archivados por VENTANA: dos toasters anclados a la misma ventana se robaban los eventos, y los de un toaster destruido iban al siguiente | cada toaster recibe los de SUS toasts; un toaster destruido ya no recibe nada, sus toasts siguen en pantalla, mudos | |
toaster: pbm_custom02 de la ventana ipo_owner | la ventana de anclaje recibía un pbm_custom02 en cada evento de toast, incluso sin ipo_receiver | solo una ventana establecida en ipo_receiver lo recibe; si había mapeado pbm_custom02 de su ventana para los toasts, establezca ipo_receiver, o mejor, retire ese código: los eventos llegan solos | |
| toaster: visualización | un toast se quedaba quieto cuando su ventana se movía; un toast de más de 800 px (una larga traza de error) NUNCA aparecía; anchura sin escalar (180 px al 200 %); actualizar un toast sobrevolado reiniciaba toda la pila | sigue a su ventana; el texto se desplaza dentro del toast limitado; anchura a escala de la pantalla; la pila sigue en pausa bajo el puntero | |
toolbar: adiciones, of_add_separator, of_move_item, of_remove_* con una clave ocupada, con / o ` | `, una barra desconocida o una dirección desconocida | 0, ignorado — una errata en el nombre de una barra creaba una barra fantasma | -5; solo main no necesita of_add_bar |
toolbar: of_clear, of_remove_bar, of_remove_item | los handles se quedaban, los atajos seguían activos, y una herramienta recreada con la misma clave recuperaba los colores de la anterior | handles liberados, atajos desactivados, colores y tooltips olvidados; vuelva a pedir un handle tras una retirada | |
toolbar: of_item con una dirección de tres niveles (una entrada de menú) | actuaba sobre la HERRAMIENTA de la barra que llevaba el nombre de la entrada | no designa nada; una entrada se controla con of_menu_item(dirección) | |
| toolbar: entrada elegida en un desplegable plegado en el chevrón | ue_menu_selected sin el padre de la cascada, bajo otro desplegable, o vacío | la dirección completa, como en la barra | |
toolbar: ii_index de una barra | nunca movía una barra hacia la derecha | la barra toma el rango pedido | |
toolbar: of_add_bar(clave, banda, rango) en un rango ya ocupado, o una banda más allá de las demás | orden indefinido; la banda se releía tal cual (5 para la segunda fila) | se inserta delante de la barra que ocupaba el rango; las bandas se reagrupan | |
toolbar: of_count, of_keys_at de una barra, rango de of_move_item | contaban los separadores, con una clave vacía | solo las herramientas: un separador no tiene ni clave ni rango | |
| toolbar: Intro en un cuadro de texto | validaba el campo Y pulsaba el botón predeterminado de la ventana | valida solo el campo; Escape le devuelve su texto validado | |
toolbar: is_shortcut releído; ii_band / ii_index de una barra desconocida | ctrl+s (normalizado); 1 | Ctrl+S, tal como se escribió; 0 | |
toolbar: color o tooltip puesto en una BARRA (of_bar) | sin efecto, o pintaba la herramienta del mismo nombre | aplicado a la barra, y solo a ella | |
| toolbar: atajo de una herramienta oculta o más allá del límite de la demo | se tragaba la tecla | la tecla va a la siguiente herramienta con el mismo atajo | |
webbrowser: una página que no se carga (host desconocido, red, certificado, of_stop) | lanzaba ue_load_completed, como un éxito | lanza ue_load_failed(as_url, al_status) en su lugar | |
webbrowser: palabras en is_address («factura 2026») | salían como https://factura 2026 y después ya no se navegaba | rechazadas con ue_error, lo siguiente se carga; una búsqueda si is_search_url está definido | |
webbrowser: una ruta de disco (C:\temp\x.html) o //host/x en is_address | precedido de https://, por tanto introuvable | file:///C:/temp/x.html; https://host/x; javascript: rechazado | |
webbrowser: is_address releído tras seguir un enlace o Atrás | la última dirección asignada | la página mostrada | |
webbrowser: of_go_back, of_go_forward, of_stop justo después de is_address | podían ejecutarse ANTES de la dirección; of_stop dejaba reanudar las páginas en espera | se ejecutan en el orden escrito; of_stop también descarta las páginas en espera | |
webbrowser: of_save_as_png, of_save_as_jpg, of_print_to_pdf, of_print | capturaban o imprimían la barra de direcciones | el sitio mostrado | |
webbrowser: enlace target=_blank, window.open tras un clic | no pasaba nada | la página se abre en la vista, ue_new_window lo indica | |
webbrowser: respuesta de of_execute_javascript de más de 4.095 caracteres | cortada sin avisar | entera | |
| webbrowser: F5, Ctrl+R, Ctrl+rueda en la barra de direcciones | recargaban o ampliaban la propia barra | sin efecto en la barra; activos en el sitio | |
webbrowser: clic derecho en un campo de texto con ib_context_menu | Atrás / Adelante / Recargar | Cortar / Copiar / Pegar | |
webbrowser: Tab u of_focus_webview sin barra de direcciones; archivo soltado en el sitio con ib_allow_drop | foco perdido en una barra de 0 px; archivo abierto por el navegador | el foco va al sitio, Tab sale de él hacia su ventana; ue_drop_files | |
pdfviewer: is_source releído, y as_source de ue_load_completed | la dirección interna /_file/?p=… | lo que usted escribió (doc\notice.pdf) | |
| pdfviewer: fichero ausente, no es un PDF, sitio inaccesible o que rechaza el marco | ue_load_completed sobre una página de error, o ningún evento | ue_load_failed(as_source, as_reason) (constantes REASON_*), el visor permanece vacío | |
pdfviewer: http://, data: de un tipo distinto de PDF, página web en https:// | mostrados (o marco vacío para http://) | rechazados: ue_load_failed con REASON_INSECURE o REASON_NOT_PDF | |
pdfviewer: un # en is_source | cortaba la ruta (Presupuesto #12.pdf no encontrado); el fragmento de una URL se mezclaba con ii_page / ii_zoom | un # forma parte del nombre del fichero; el fragmento de una URL se ignora, página y zoom pasan por las propiedades | |
pdfviewer: ii_page asignado antes de is_source | perdido: el documento se abría en la página 1 | conservado para ese documento; sin página asignada desde el último is_source, un documento nuevo se abre en la página 1 | |
pdfviewer: PDF regenerado en la misma ruta y luego of_refresh; PDF de 32 MB o más; ruta de más de 260 caracteres | la versión anterior durante 5 minutos; nada; no encontrado | el fichero tal como está en el disco; mostrado (servido por partes); mostrado | |
pdfviewer: enlace seguido en el documento; of_print | el sitio reemplazaba el documento, sin vuelta atrás; imprimía la página que enmarca el documento | el documento permanece, ue_link_clicked(as_url) le da la dirección; of_print imprime el documento (-4 sin documento) | |
picture, button, tilesbox: il_badge_color | 0 = el color del tema; el negro era inalcanzable; sin fijar, se releía 0 | -1 = sin fijar (el color del tema), como cualquier otro color; 0 es el negro. Sustituya il_badge_color = 0 por -1 | |
picture: id_zoom fijado antes de ib_zoomable | se releía como se fijó, mientras la imagen seguía encuadrada | ignorado: id_zoom se queda en 1.0 mientras ib_zoomable sea falso; fije primero ib_zoomable | |
picture: ib_mirror sobre una imagen girada un cuarto de vuelta; is_align / is_valign con una rotación | volteo vertical en pantalla; la colocación giraba con la imagen | espejo y colocación se leen en pantalla, sea cual sea la rotación | |
| picture: arrastrar la imagen ampliada; rueda; imagen desactivada (statictext desactivado también) | ue_clicked en cada desplazamiento; zoom hacia el centro, la imagen podía salir del marco; los archivos soltados seguían aceptándose | ningún clic tras un desplazamiento; zoom hacia el puntero, la imagen cubre siempre su marco; desactivada, rechaza cualquier depósito; ue_error nombra la fuente | |
xml: una lectura (of_get_value, of_count, of_is_valid…) o un cambio tras una construcción | actuaba sobre el último documento CARGADO, o sobre nada | el documento construido se carga y pasa a ser EL documento; el of_start_element siguiente empieza uno nuevo | |
xml: of_load fallido (vacío, mal formado), of_start_element tras una carga | el documento anterior seguía legible y modificable (of_is_valid verdadero); un documento a medio construir seguía en of_xml | no queda nada | |
xml: of_xml, of_pretty, of_get_values de un documento grande | cortado sin avisar más allá de unos MB | entero | |
xml: of_get_value de un XPath de valor (count(//line), sum(//@qty)) | vacío | el valor como texto (3, true) | |
| xml: un XPath cuyo prefijo se declara por debajo de la raíz (respuesta SOAP) | vacío sin of_register_namespace | resuelto | |
xml: is_last_error tras un XPath no válido, una página que no responde, un rechazo; códigos de of_register_namespace / of_clear_namespaces | vacío, o «no node matched» fuera cual fuera la causa; los cambios devolvían -4 cuando faltaba la página; siempre 0; una página sin respuesta devolvía el texto null | la verdadera razón; -2 cuando la página no responde; un URI vacío se rechaza (-5); vacío en lugar de null | |
xml: of_pretty | perdía CDATA, comentarios, instrucciones de procesamiento y la declaración XML, y rehacía el texto mixto | todo se conserva; un elemento que contiene texto queda en una línea | |
| xml: construcción — salto de línea o tabulación en un atributo, CR en un texto, carácter de control, nombre no válido | releídos alterados; documento mal formado; nombre escrito tal cual | releídos idénticos; carácter de control eliminado; nombre rechazado (is_last_error), el elemento se omite | |
xml: un documento que contiene un elemento <parsererror>; mensaje de un XML mal formado | rechazado como mal formado; mensaje enmarcado por «This page contains the following errors» | cargado; solo la línea de error | |
xml: of_remove de nodos anidados, de la raíz, de un atributo; of_set_value("/"); of_add_child bajo un atributo | contaba dos veces; vaciaba el documento; el atributo seguía; 0 sin efecto; «parent not found» | el recuento real; la raíz se queda (0 e is_last_error); el atributo se quita; -4; «the node cannot have children» | |
xml: of_close y luego una lectura; ipo_owner fijado tras la primera lectura | el documento cargado se perdía; ignorado hasta of_close | el documento se conserva y se vuelve a cargar; transmitido en la lectura siguiente | |
temas metro-light y metro-dark: texto sobre el color de acento | blanco sobre el azul #25a0da en ambos temas (cabeceras de panel, pestaña activa, botón de aplicación de la cinta, mosaicos): poco legible | metro-light: el acento pasa al azul más profundo #1b76a1, texto siempre blanco; metro-dark: el acento sigue siendo #25a0da, el texto encima pasa a casi negro (#1b1b1b) | |
temas office-dark, office2003-dark, office2003-light, office2007-light: colores de acento y de cabecera | en modo oscuro, el acento se leía mal sobre la página (viñeta de paso, insignia), igual que el texto blanco sobre el acento al pasar el ratón y sobre las cabeceras de columna del crosstab; en modo claro, la parte alta de los degradados del botón de aplicación y de las cabeceras de panel era demasiado pálida para el texto blanco | office-dark: acento #1178d1, paso del ratón #0f6cbd; office2003-dark: acento #4574cb, paso del ratón #2f5aa8; cabeceras del crosstab más oscuras al pasar el ratón y en los totales; office2003-light y office2007-light: inicio del degradado más profundo (#3e73cf, #4275c5), cabeceras de panel de office2007-light más opacas. Texto siempre blanco, al menos 4,5:1 | |
texto sobre un color elegido por la aplicación (il_accent, color de un elemento, cabecera de panel, insignia) | blanco hasta un color bastante claro: un azul medio como #25a0da recibía texto blanco a 2,95:1 | blanco o casi negro (#1b1b1b), el que mejor se lea (relación de contraste WCAG): #25a0da recibe ahora texto oscuro | |
jsontree: of_clear | vaciaba el árbol Y devolvía ib_wrap e ib_search_enabled a sus valores por defecto | solo vacía el árbol; los ajustes se mantienen (es of_reset el que lo restablece todo) | |
jsontree: is_json = "" | lanzaba ue_error «Invalid JSON» | vacía el árbol sin error, como prometía la documentación | |
jsontree: números y claves mostrados, is_json releído | reescritos por el motor: 12345678901234567890 mostrado 12345678901234567000, 1.10 → 1.1, la primera de dos claves idénticas perdida; is_json se releía compactado, y vacío tras un JSON inválido | tal como se recibieron, las dos claves incluidas; is_json se relee tal como se fijó | |
jsontree: of_match_count, al_count de ue_search_result | contaban LÍNEAS; dos ocurrencias en una línea eran ambas «actuales» | cuentan OCURRENCIAS, solo una es la actual y of_search_next pasa de una a otra | |
| jsontree: bloques abiertos por una búsqueda | TODOS los que contenían una coincidencia, para siempre: buscar «e» abría todo el documento | solo los que ocultan la ocurrencia ACTUAL; of_clear_search devuelve los pliegues de antes de la búsqueda | |
jsontree: of_select_path | una línea oculta en un bloque plegado seguía invisible; una ruta desconocida devolvía 0 | los bloques que la ocultan se abren; una ruta desconocida devuelve -5 y la selección no se mueve | |
| jsontree: plegar el bloque que contiene la selección | la selección se volvía invisible, y la flecha siguiente volvía a empezar desde arriba | la selección pasa al bloque plegado; con un gesto del usuario, ue_selection_changed lo indica | |
xmltree: of_clear | vaciaba el árbol Y devolvía ib_wrap e ib_search_enabled a sus valores por defecto | solo vacía el árbol; los ajustes se quedan (es of_reset el que restablece todo) | |
xmltree: is_xml = "" | lanzaba ue_error «Invalid XML» y mostraba un error rojo | vacía el árbol sin error, como la documentación prometía | |
xmltree: is_xml relu | reserializado por el motor: comillas cambiadas, saltos de línea perdidos, vacío tras un XML inválido | se relee tal como se asignó, incluso un texto inválido | |
| xmltree: ce qui s'affiche | un CDATA era invisible (el elemento aparecía vacío), faltaban comentarios, instrucciones de procesamiento y declaración, el texto de un contenido mixto se pegaba antes de los hijos, un texto perdía sus espacios, un valor de atributo con comillas se mostraba tal cual | cada nodo en su sitio, en el orden del documento; la línea sigue siendo XML (comilla elegida o ", , &, <); los espacios significativos se quedan | |
xmltree: of_match_count, al_count de ue_search_result | contaban LÍNEAS; una coincidencia a caballo entre dos trozos (id="4152") se contaba sin resaltarse | cuentan APARICIONES, cada una resaltada, solo una es la actual | |
| xmltree: éléments ouverts par une recherche | TODOS los que contenían una coincidencia, para siempre | solo los que ocultan la aparición ACTUAL; of_clear_search devuelve los pliegues de antes de la búsqueda | |
xmltree: of_select_path | una línea oculta en un elemento plegado seguía invisible; una ruta desconocida devolvía 0 | los elementos que la ocultan se abren; una ruta desconocida devuelve -5 y la selección no se mueve | |
xmltree: ue_selection_changed | se lanzaba también sin cambio (Fin en la última línea); plegar el elemento que contenía la selección la volvía invisible, sin aviso | se lanza solo cuando la selección cambia; al plegar, la selección pasa al elemento plegado y el evento lo dice | |
xmltree: ue_error | el texto bruto de la página de error del motor; un elemento <parsererror> del documento pasaba por un error | solo la línea del error («error on line N at column M: …»); un <parsererror> del documento es un elemento como otro | |
| restclient: cookies de una petición síncrona | ninguna guardada: cada of_get / of_post abría su propia sesión; ib_keep_cookies solo valía para lo asíncrono | una sesión por objeto, común a lo síncrono y lo asíncrono: un inicio de sesión síncrono le mantiene conectado para las llamadas siguientes | |
restclient: ib_keep_cookies cambiado tras la primera petición | sin efecto hasta of_close | se aplica de inmediato; a false, las cookies ya guardadas se olvidan | |
| restclient: redirección hacia otro host o puerto | solo caían Authorization, Cookie y la autenticación Basic: una clave de API (X-Api-Key…) iba al tercero | ninguna cabecera puesta con of_set_header sigue, solo queda Content-Type | |
restclient: of_set_bearer("") tras of_set_basic; of_set_basic y luego of_set_bearer; of_set_basic("", "") | la contraseña Basic seguía enviándose; dos Authorization a la vez; se enviaba Basic Og== | una sola autenticación a la vez: cada una reemplaza a la otra, un token o un usuario vacío la retira | |
restclient: HEAD, o respuesta 204 / 304 que anuncia un tamaño | -4 «connection lost», y repetida en asíncrono | el estado (200, 304…) | |
restclient: cuerpo con un byte no válido; charset=iso-8859-1 o utf-16 | todo el cuerpo releído en Windows-1252; charset ignorado | solo el byte no válido se vuelve un carácter de reemplazo; se lee el charset anunciado | |
| restclient: cuerpo de texto de varios cientos de MB | guardado tres veces en memoria: un proceso de 32 bits podía morir | -4 «body too large : use of_download» más allá de 64 MB | |
| restclient: cien peticiones asíncronas lanzadas de golpe | cien hilos en paralelo | seis como máximo a la vez, las siguientes esperan su turno | |
| restclient: una petición asíncrona enviada más a menudo que cada 50 ms (un temporizador de telemetría) | ninguna respuesta entregada mientras duraba el flujo | las respuestas llegan a medida que vienen | |
restclient: of_cancel de un id desconocido; de una petición cuya respuesta ha llegado | -1; 0 (pero llegaba ue_response) | -5; -4, demasiado tarde | |
restclient: of_response_text() tras of_download; tras un of_download(url, "") | el número de bytes escritos; la respuesta anterior | vacío: ese cuerpo está en el archivo; vacío | |
restclient: leer una respuesta asíncrona durante ue_progress | una memoria en escritura: posible cierre | vacío, of_status(id) = 0 mientras la petición está en curso | |
video: cambiar de película (is_source, of_play(source)) | el segmento, los subtítulos, marcadores y capítulos de la película anterior se quedaban | se van con ella (fijados antes de cualquier película, esperan a la primera); una película de la lista conserva sus subtítulos. Fíjelos después de is_source | |
video: of_go_chapter, of_next_chapter, of_previous_chapter | lanzaban ue_seeked; of_go_chapter más allá del número de capítulos devolvía 0 sin hacer nada | ue_seeked y ue_chapter_changed, como un clic en la barra; -5 | |
video: of_add_source con un is_source fijado | la lista nunca empezaba, of_next iba a la primera película | la primera entrada se carga enseguida (salvo que la película fijada SE ESTÉ REPRODUCIENDO: la lista la sigue al terminar) | |
video: ue_started tras of_stop o el final de la película, luego of_play | nunca: una vez por fuente | en cada nueva reproducción (no al reanudar una pausa) | |
video: of_play sobre una película en error; of_reset; ii_rate en la película siguiente | «en curso» para siempre, sin evento; la película anterior seguía cargada (of_duration, of_capture); vuelta a la velocidad normal | ue_failed de nuevo; la película se descarga; la velocidad se mantiene | |
video: of_capture a un archivo que no se puede escribir; a «\x» o «C:x» | -4 (la doc decía -5); escritos según la carpeta actual | -4, y no queda ningún archivo truncado; -5: dé una ruta completa | |
| video: pantalla completa y luego Alt+Tab a otra aplicación | la película seguía por encima de todo | la otra aplicación pasa delante; volver a la suya pone de nuevo la película por encima de todo | |
| pdfviewer: un clic simple en un enlace del PDF | nada: solo Ctrl+clic lanzaba ue_link_clicked | ue_link_clicked(url), el documento sigue mostrado | |
webbrowser: una página data: (is_address = "data:text/html,…") | mostrada, pero ningún ue_load_completed | ue_load_completed con la dirección pedida | |
webbrowser: una nueva ventana pedida por el sitio hacia file:, data: o mailto: | abierta en la vista, ue_new_window lanzado | rechazada: ue_error, sin ue_new_window; solo se abren http y https | |
| webbrowser: un sitio que pide la cámara, el micrófono, la ubicación, las notificaciones | aviso del navegador al usuario, respuesta recordada | rechazado salvo que ue_permission_requested devuelva true; no se recuerda nada | |
| webbrowser: cookies, sesiones y almacenamiento de los sitios | en el perfil compartido con los componentes | en un perfil de navegación propio: una sesión iniciada en 3.0 no se conserva (volver a conectarse una vez); of_clear_browsing_data la borra | |
webbrowser: is_search_url sin %s, sin http(s):// o en javascript: | aceptado: cada búsqueda fallaba o perdía el texto escrito | rechazado: ue_error, el motor vigente se mantiene | |
webbrowser: gestion/ o [::1]/x en is_address | «no es una dirección» (o una búsqueda) | https://gestion/, https://[::1]/x; la palabra sola sigue siendo texto | |
webbrowser: clic derecho sobre una selección, un vínculo, una imagen con ib_context_menu | solo Atrás / Adelante / Recargar | además: Copiar, Abrir vínculo, Copiar dirección del vínculo, Copiar imagen | |
tab: pestaña cerrable desactivada (ib_enabled = false) | la cruz desaparecía pero su hueco quedaba vacío | ni cruz ni hueco: la pestaña mide como una no cerrable | |
video: of_chapters con un título que contiene ` | ` o un tabulador | partido en dos capítulos | leído tal cual |
video: un .srt en Windows-1252 o UTF-16 | acentos ilegibles; ningún subtítulo | leído correctamente | |
commandpalette: of_open sin runtime WebView2; toaster: of_show sin runtime | -1 | -6, como el cuadro de mensaje | |
crypto, xml, restclient: of_open cuando la página o el cliente no puede crearse | el código bruto de la DLL (-1, -3…) | -2, e is_last_error dice por qué |
Si había sorteado alguno de estos defectos — por ejemplo tratando ue_menu_selected con una dirección vacía para recuperar los clics de un grupo plegado —, retire el rodeo: el evento documentado llega ahora.
11.4 El generador n_pbt_menu2ribbon #
En la 3.0, el código escrito por n_pbt_menu2ribbon usaba métodos de handle que ya no existían: no compilaba sin retoques. En la 4.0 escribe la API actual — cada adición por su dirección, desde el componente —, retoma los submenús a cualquier profundidad, conserva los elementos atenuados y marcados, y su enrutador cubre ue_clicked, ue_toggled y ue_menu_selected. Las claves cambian: cada una es ahora el ClassName del elemento de menú (m_fichier/g1/m_ouvrir) en lugar de la ruta con puntos (m_principal.m_fichier.m_ouvrir).
La receta: una cinta ya construida a mano, o a partir de un código generado y luego corregido, no tiene nada que cambiar. Para partir de nuevo del generador, vuelva a lanzarlo y pegue también el nuevo enrutador: el antiguo leía claves con puntos.
11.5 Lo que se ha añadido #
Nada de lo siguiente pide modificar su código. Los nuevos componentes de la 4.0 se presentan en las notas de la versión.
| Objeto | Añadido |
|---|---|
| ribbon | of_add_menu_separator acepta la dirección de una entrada: la línea va a la cascada situada debajo |
| ribbon | of_add_menu_item y of_add_menu_check aceptan una cascada de cualquier profundidad, y cada nivel se gobierna con of_menu_item(dirección) |
| menubar | of_add_separator acepta la dirección de una entrada (file/export): la línea va a su cascada; of_menu(...) lleva los colores (il_accent, il_back_color…) y la información del título |
| crosstab | of_set_label_filter(as_field, as_type, as_a, as_b) sobre texto (LABEL_CONTAINS, LABEL_BEGINS, LABEL_ENDS) y las constantes LABEL_GT, LABEL_LT, LABEL_BETWEEN: la forma con dos double solo podía pasar un número |
| crypto | KEYFORMAT_TEXT, KEYFORMAT_HEX, KEYFORMAT_BASE64 y una sobrecarga de of_hmac, of_jwt_sign y of_jwt_verify que los toma (una clave en BYTES: secreto entregado codificado, AWS SigV4); una entropía opcional para of_protect y of_unprotect |
| crypto | of_hmac_verify (un HMAC recibido — webhook — comparado en tiempo constante); of_jwt_header y una sobrecarga de of_jwt_sign que añade miembros a la cabecera (kid); of_totp_verify(…, ref al_step), el paso aceptado, para rechazar un código ya usado; il_password_hash_rounds |
| messagebox | of_get_last_error(): por qué no se pudo abrir el último cuadro (cadena vacía si se abrió) |
| n_pbt_utils | of_dir_has_subfolders(as_dir, ab_include_hidden): la misma pregunta, contando también las carpetas ocultas, de sistema y las uniones |
| n_pbt_utils | of_dw_date_order(): el orden en que un DataWindow escribe una fecha en este equipo (dmy, mdy, ymd) |
| speechout | of_is_paused(): verdadero mientras la lectura está en pausa |
| Texto enriquecido | la etiqueta [symbol=nombre]: dieciocho glifos monocromos que siguen el color del texto (capítulo 4) |
| toolbar | of_menu_item(dirección) → n_pbt_toolbar_menu_item: cada entrada de menú desplegable tiene su estado (ib_enabled, ib_visible, ib_checked, is_text, is_shortcut), designada por su dirección completa |
| toolbar | is_date en un selector de fecha; of_clear_menu; of_remove_item de una entrada de menú; of_add_menu_separator en una entrada (cascada); cascadas de cualquier profundidad |
| pdfviewer | ue_load_failed(as_source, as_reason) y las constantes REASON_*; ue_link_clicked(as_url); las direcciones file:///; ib_allow_save e ib_allow_print: los comandos Guardar e Imprimir de la barra del lector |
| picture | is_alt_text (texto alternativo que lee un lector de pantalla); un teclado para el zoom: +, -, 0 y las flechas para desplazarse; n_pbt_utils.of_json_get_str_array |
| webbrowser | is_search_url (motor de búsqueda para las palabras que no son una dirección); ib_veto_new_window y ue_new_window; ib_veto_downloads y ue_download_starting; ue_load_failed; ue_error; of_show_html; of_clear_browsing_data e ib_private; ib_veto_navigation y ue_navigating; is_title y ue_title_changed; ue_permission_requested y las constantes PERMISSION_*; las constantes LOADSTATUS_* de ue_load_failed; F5, Ctrl+R, Alt+flechas y Esc en la barra de direcciones |
| jsontree | of_selected_text devuelve el valor DECODIFICADO de la hoja seleccionada (una cadena sin comillas ni escapes); of_has_selection distingue la raíz, cuya ruta es "", de «nada seleccionado»; el teclado salta las llaves de cierre (ya no se seleccionan) |
| xmltree | cada atributo es un objetivo: un clic da su ruta (/order/@id), of_select_path lo selecciona, of_selected_value devuelve su valor; un documento grande solo dibuja las líneas en pantalla (8.000 elementos hermanos: 12 s → 0,15 s, una flecha en pocos ms) |
| video | ue_muted_changed(ab_on): el usuario silenció o devolvió el sonido (el altavoz de la barra, la tecla M), como el volumen y la velocidad ya tenían su evento |
| ribbon | of_remove_menu_item, of_clear_menu, of_remove_combo_item, of_clear_combo, of_remove_gallery_item, of_remove_app_menu_item, of_remove_contextual_group: las retiradas bajo un control — una lista «Archivos recientes» se actualiza sin volver a crear el control |
| ribbon | of_app_menu_item(dirección): el handle de una entrada del menú de aplicación (is_label, is_image, ib_enabled, ib_checked, ib_visible); of_app_menu_item("") recorre el menú |
| ribbon | teclas de acceso rápido de varias letras (is_keytip = "FP"), tecleadas una tras otra; nombres accesibles (barra de acceso rápido, mosaicos) para los lectores de pantalla |
| toolbar | of_add_split_button: un botón dividido (acción + menú); is_group: conmutadores exclusivos (alineación); is_image en una herramienta y en una entrada de menú; ue_item_rclicked(dirección): el clic derecho de una herramienta |
11.6 Nombres y recuentos alineados con el resto de la biblioteca #
Siete lugares hablaban de otra manera que el resto de la biblioteca. La 4.0 los alinea en lugar de mantener la diferencia: cambian dos nombres — el compilador le muestra cada línea que retocar —, la ruta que devuelve el árbol JSON pasa a ser la de n_pbt_json, la del árbol XML bajo un espacio de nombres por defecto un XPath que funciona, dos recuentos empiezan ahora en 1, como toda posición del producto, y un evento ambiental pasa a ser opcional. Son los únicos cambios de la 4.0 que piden retocar código que compilaba.
| Qué | 3.0 | 4.0 |
|---|---|---|
| progressbar: límites de la escala | il_minimum, il_maximum, unos long | id_minimum, id_maximum, unos double: una escala 0..1 o un número de bytes superior a 2 GB es válido; el prefijo dice el tipo |
| json: índice de arreglo en una ruta | desde 0: lines/0 es la primera línea | desde 1: lines/1 es la primera línea; lines/0 no designa nada — una lectura lo da por ausente, una escritura devuelve -5 |
jsontree: ruta de un nodo (ue_node_clicked, ue_selection_changed, of_selected_path, of_select_path) | niveles unidos por un punto, índice de array desde 0: lines.0.sku — un dialecto que n_pbt_json no leía | la ruta de n_pbt_json: niveles unidos por /, índice desde 1 (lines/1/sku), "" para la raíz — para pasarla tal cual a of_get_string sobre el mismo texto |
xmltree: ruta de un elemento bajo un espacio de nombres POR DEFECTO (xmlns="urn:…") | /Envelope/Body — un XPath que no encuentra nada | /*[local-name()='Envelope']/*[local-name()='Body'] — para pasar tal cual a n_pbt_xml.of_get_value; un elemento sin espacio de nombres o con prefijo conserva su ruta |
toolbar: band y index de la disposición (of_get_layout, of_set_layout, ue_layout_changed) | desde 0, una cadena llamada «opaca» mientras ii_band, ii_index y ue_bar_reordered contaban desde 1 | desde 1, como ii_band, ii_index y ue_bar_reordered; una ida y vuelta lectura → restauración devuelve la misma cadena |
statictext: ue_cycle | lanzado en cada bucle del desplazamiento, sin suscripción — y también por una animación dentro del texto | un evento ambiental, por tanto opcional: lanzado solo cuando ib_track_cycle = true, como ue_mouse_enter con ib_track_mouse; solo en cada pasada completa del texto |
tilesbox: 2.º argumento de ue_tile_moved | as_to_keys, aunque lleva UNA clave: el grupo de destino | as_to_key: el nombre dice lo que lleva, como en todas partes (as_key = un nivel, as_keys = una dirección) |
restclient: ue_failed | ue_failed (long al_id, string as_error) | ue_failed (long al_id, long al_code, string as_error): -4 fallo, -5 URL o archivo — ya no hace falta analizar el texto |
restclient: of_json_value | una clave, primera aparición a cualquier profundidad; solo un valor de texto — un número devolvía el nombre de la clave siguiente | una ruta n_pbt_json ("json/customer", "items/1/qty"), cualquier valor: "4152", "true". Una clave anidada se nombra con sus padres |
La receta: (1) recompile — el compilador señala il_minimum, il_maximum y as_to_keys: renómbrelos id_minimum, id_maximum y as_to_key; (2) busque las rutas json que apuntan a un arreglo y sume 1 a cada índice — un bucle for i = 0 to n - 1 sobre "lines/" + String(i) pasa a for i = 1 to n; (3) una disposición de toolbar guardada por la 3.0 cuenta desde 0: sume 1 a band y a index de cada barra antes de pasarla a of_set_layout, o deje que el usuario reorganice una vez — releída tal cual, juntaría sus dos primeras bandas; (4) donde ue_cycle esté programado, ponga ib_track_cycle = true en la construcción; (5) donde leía una ruta del árbol JSON (ue_node_clicked, of_selected_path) o daba una (of_select_path), sustituya los puntos por / y sume 1 a cada índice de array — lines.0.sku pasa a ser lines/1/sku, que va tal cual a n_pbt_json.of_get_string.
11.7 Los temas siguen a la aplicación #
La revisión de los temas ha hecho cumplir a cada componente la regla que el capítulo 4 ya anunciaba: la aplicación decide el tema y el acento, un componente solo se aparta de ellos en lo que se le pide. No se renombra ningún método; lo que cambia se ve en pantalla, al releer las propiedades y en dos códigos de retorno.
| Qué | 3.0 | 4.0 |
|---|---|---|
PBT_SetDefaultTheme | cambiar de tema borraba el acento fijado con PBT_SetDefaultThemeAccent — incluso al volver a fijar el mismo tema — y aplanaba el fondo degradado de office2007 y office2003 | el acento de la aplicación se mantiene de un tema a otro; cada tema conserva su fondo |
PBT_GetDefaultThemeAccent | el acento del tema cuando la aplicación no había fijado ninguno | -1 mientras la aplicación no haya fijado un acento |
| códigos de retorno nativos | PBT_SetDefaultTheme con un nombre desconocido devolvía 0 y dejaba sin tema a los componentes siguientes; PBT_SetDefaultThemeAccent con un color de sistema de PowerBuilder lo pintaba de negro | -5 en ambos casos; el tema o el acento anterior se mantiene |
| un eje de tema fijado solo | congelaba el otro eje en el tema de la aplicación del momento | el eje vacío sigue el tema de la aplicación en cada uno de sus cambios; los dos ejes vacíos = el tema de la aplicación, por completo |
is_theme_style · is_theme_mode · il_theme_accent | se releían "", "" y -1, se hubiera fijado lo que fuera | el estilo y el modo mostrados, y el acento propio del componente (-1 si no tiene) |
un valor de eje desconocido ("office2010") o mal escrito (" Metro ") | el componente perdía su tema y dejaba de escuchar el de la aplicación hasta el of_reset() | espacios y mayúsculas ignorados; un valor desconocido se ignora y el eje conserva su valor |
| acento de un componente con tema local | conservaba el acento de su tema, incluso cuando la aplicación fijaba uno | sigue el acento de la aplicación mientras no tenga el suyo |
| un acento personalizado | solo cambiaba el acento: el paso y la pulsación de los botones de acento, las selecciones teñidas, los botones marcados de la cinta conservaban el acento del tema | todo lo que deriva del acento lo sigue |
tilesbox : il_badge_color | 0 y -1 = el color del tema; releído 0 sin color fijado; el negro era inalcanzable | -1 = el color del tema (releído -1); 0 es el negro, como en todos los colores |
un color de sistema de PowerBuilder (ButtonFace…) en il_theme_accent | pintado de negro | contado como -1: el acento de la aplicación |
webbrowser · pdfviewer | el contenido de terceros seguía el modo claro/oscuro de Windows, y un rectángulo blanco precedía su primera visualización | sigue el tema predeterminado de la aplicación, sobre el fondo del tema (4.11) |
| acento de office-dark y office2003-dark | un azul oscuro, legible a 3:1 cuando coloreaba un texto (enlaces, [accent]) | un azul más claro (#479ef5, #7ea1e8), con un texto oscuro encima |
datagrid: líneas entre filas | ausentes en ocho temas de diez | presentes en los diez temas, con el color de cuadrícula del tema |
| contraste alto de Windows | los iconos monocromos y los anillos dibujados como sombra desaparecían | admitido (4.10) |
La receta: (1) si volvía a fijar el acento después de cada PBT_SetDefaultTheme, ya no hace falta — y si contaba con un cambio de tema para borrar el acento, llame a PBT_SetDefaultThemeAccent(-1); (2) compruebe el código de retorno de PBT_SetDefaultTheme cuando el nombre venga de una entrada o de un archivo; (3) donde fijaba los dos ejes en cada componente para seguir a la aplicación, déjelos vacíos; (4) un mosaico cuyo distintivo volvía «al tema» con il_badge_color = 0 se escribe ahora -1.
11.8 crypto: lo que la seguridad rechaza ahora, y adónde van las rutas relativas #
La revisión de seguridad de crypto cierra una puerta real — un JWT falsificado con la clave PÚBLICA pasaba cuando faltaba el algoritmo — y sustituye varios silencios por un rechazo que dice su motivo. No se retira ningún método; una firma ECDSA cambia de forma, y las rutas relativas de los archivos siguen una sola regla.
| Qué | 3.0 | 4.0 |
|---|---|---|
of_jwt_verify, of_jwt_sign | un algoritmo VACÍO valía JWT_HS256, y una clave PEM se aceptaba como secreto HS256: verificado con la clave PÚBLICA como secreto, un token falsificado pasaba; el algoritmo de la cabecera se comparaba sin mayúsculas, una cabecera crit se ignoraba | el algoritmo es OBLIGATORIO y se compara EXACTAMENTE; una clave PEM nunca es un secreto HMAC (of_hmac también); crit se rechaza — cadena vacía e is_last_error |
of_jwt_sign | un exp dado en los claims se sustituía en silencio por la duración al_expires_seconds | ambos a la vez se rechazan: quédese con uno u otro |
of_encrypt, of_decrypt | una contraseña VACÍA cifraba — cualquiera releía el valor | rechazada, como ya hacía of_encrypt_file; of_password_hash("") sigue permitido |
il_pbkdf2_rounds | un valor fuera de 1.000 a 10.000.000 volvía en silencio a 100.000 | la llamada se RECHAZA: cadena vacía para un texto, -5 para un archivo, e is_last_error dice los límites |
of_password_hash | il_pbkdf2_rounds (100.000 rondas) | il_password_hash_rounds, 600.000 por defecto (la cifra de OWASP); los valores ya almacenados llevan sus rondas y siguen verificándose |
of_base64_decode, of_base64url_decode, of_hex_decode, of_decrypt… | bytes que no son un texto (un PDF, una imagen) volvían como caracteres de sustitución, sin error | cadena vacía e is_last_error, que remite a of_base64_decode_to_file o of_decrypt_file |
of_sign (clave EC) | una firma IEEE-P1363 (64 bytes), que openssl dgst y Java rechazan | una firma DER; of_verify lee ambas formas, una firma ya almacenada sigue verificándose; el JWT ES256 sigue en P1363 |
of_totp_code, of_totp_verify | un carácter no base32 en el secreto (un 0 tecleado por una O) se quitaba en silencio: OTRO secreto, códigos que nunca coincidían | rechazado (espacios y = tolerados) |
of_encrypt_file | «cualquiera que sea su tamaño» | hasta 64 GB, el límite de un contenedor AES-GCM: más allá, -7 |
rutas relativas: n_pbt_crypto, u_pbt_zip, of_capture del reproductor de vídeo | leídas en la carpeta ACTUAL (que se mueve tras un DirList o un cuadro de archivos), escritas junto al EXE (en el IDE: la carpeta de PowerBuilder) | LEÍDAS en la carpeta actual, luego en la carpeta en la que arrancó la aplicación, luego junto al EXE; ESCRITAS en la carpeta en la que arrancó la aplicación — la misma en el IDE y compilada |
La receta: (1) pase SIEMPRE el algoritmo de un JWT como constante (n_pbt_crypto.JWT_RS256), nunca leído del token ni de un INI al que le puede faltar; (2) si un servidor verificaba sus firmas EC en P1363 (.NET VerifyData por defecto), conviértalas o verifíquelas en DER (DSASignatureFormat.Rfc3279DerSequence); (3) un il_pbkdf2_rounds fijado fuera de límites debe volver entre 1.000 y 10.000.000; (4) un archivo que una ruta relativa escribía junto al EXE está ahora en la carpeta de arranque de la aplicación — dé una ruta completa si quiere otra.
11.9 restclient: cabeceras que dicen su rechazo, redirecciones que conservan la petición #
La revisión de restclient sustituye dos pérdidas silenciosas — una modificación nunca hecha tras una redirección, una autenticación nunca enviada tras una cabecera mal formada — por el comportamiento correcto o un rechazo que dice su motivo. No se retira ningún método; tres pasan a ser funciones, llegan dos propiedades, y la autenticación de Windows solo sale si usted la pide. Un punto vale para los cinco componentes no visuales que lanzan eventos: un error en su código de evento ahora se ve.
| Qué | 3.0 | 4.0 |
|---|---|---|
of_set_header, of_set_bearer, of_set_basic | subrutinas: no se devolvía nada, un valor mal formado se guardaba tal cual | funciones long: 0, o -5 y nada cambia — nombre vacío, nombre con un espacio, dos puntos o un carácter de control, valor con un salto de línea; un valor vacío retira la cabecera |
| una petición con una cabecera mal formada | salía SIN NINGUNA cabecera — Authorization incluida —, sin error | -5, e is_last_error = «invalid header: <nombre>»; no sale nada |
| redirección 301 / 302 de un PUT, PATCH o DELETE | seguía en GET sin cuerpo: la modificación nunca se hacía, y la llamada devolvía 200 | método y cuerpo conservados; solo un POST se convierte en GET; un 303 sigue en GET sin cuerpo (salvo HEAD) |
| una conexión reutilizada (la de una redirección) que el servidor cierra en el mismo instante | -4 y «request failed: 12152», de vez en cuando | la petición se reenvía UNA vez, cuerpo incluido, por una conexión nueva: el servidor no había respondido nada |
of_response_text ( ), of_response_headers ( ), of_response_header, of_json_value | la última respuesta síncrona del PROCESO: un segundo n_pbt_restclient la sobrescribía | la del OBJETO; of_close la olvida |
| cuerpo leído como texto, aplicación de 32 bits | limitado a 64 MB | limitado a 16 MB (64 MB en 64 bits): más allá, -4 «body too large» — léalo con of_download |
of_get_async, of_request_async… con una URL o una cabecera no válida | un id de petición, luego un ue_failed | -5 al instante, no sigue ningún evento; is_last_error dice por qué («invalid URL: …») |
| autenticación de Windows (Negotiate / NTLM) | el sistema respondía por sí solo al desafío de un servidor de la intranet con las credenciales de la sesión | desactivada por defecto: las credenciales no van a ningún servidor sin ib_windows_auth = true (propiedad nueva); is_client_certificate (nueva) pone un certificado de cliente |
of_cancel (long al_id), of_status (long al_id), of_response_text (long al_id)… | el id de una petición de OTRO objeto alcanzaba esa petición | solo las peticiones del objeto: of_cancel devuelve -5 y no cancela nada, los lectores devuelven 0 / "" |
of_status (long al_id) en ue_failed | podía devolver 200: el estado de una respuesta recibida y luego perdida | 0 para toda petición fallida |
ue_failed | -4 fallo, -5 URL o archivo | -4 fallo, -5 archivo o certificado de cliente, -6 petición de rango rechazada en demo |
error de ejecución en un evento de la aplicación — restclient, speechout, soundplayer, toaster, commandpalette | tragado sin decir nada, y los eventos siguientes dejaban de entregarse | llega al evento SystemError de la aplicación como cualquier error de script; los eventos siguientes se entregan |
il_max_retries | reintentaba también un fallo que se repite de forma idéntica (demasiadas redirecciones, redirección no válida o HTTPS → HTTP, TLS, archivo imposible de escribir) | esos fallos ya no se reintentan; 429, 503 y un fallo de red siguen haciéndolo |
| URL y rutas | el fragmento #… iba al servidor; una Location relativa ?page=2 perdía el último segmento; una ruta relativa de of_download seguía la carpeta actual del momento | el fragmento ya no se envía; Location resuelta como lo hace un navegador (RFC 3986); una ruta relativa se resuelve en la llamada, en la carpeta actual de la aplicación al cargar la biblioteca |
La receta: (1) pruebe lo que devuelven of_set_header, of_set_bearer y of_set_basic cuando el valor viene de una entrada, de un INI o de una base de datos — una llamada que ignora el retorno sigue compilando, pero el -5 es ahora la única huella de una cabecera rechazada; (2) una API de intranet con autenticación de Windows que respondía 200 ahora responde 401: ponga ib_windows_auth = true en ESE cliente; (3) una aplicación de 32 bits que lee como texto un cuerpo de más de 16 MB pasa a of_download; (4) si compartía un id de petición entre dos objetos, lea la respuesta en el objeto que la envió; (5) un error que ahora aparece en SystemError desde ue_response o ue_failed ya existía: se tragaba.
11.10 speechout: ajustes vivos, una destrucción silenciosa #
La revisión de speechout cumple dos promesas que la documentación hacía y el código no — un ajuste cambiado durante una lectura vale para la frase siguiente, of_speak_from más allá del texto devuelve -5 — y retira un evento levantado en mal momento. No se retira ningún método. Añadidos: of_add_abbreviation, of_clear_abbreviations, el evento ue_mark y dos etiquetas, [mark=nombre] y [rate=150]…[/rate].
| Qué | 3.0 | 4.0 |
|---|---|---|
of_speak_from (al_index) | más allá del texto, la lectura en curso se CORTABA, nada volvía a empezar, y el retorno era 0 | -5, y la lectura en curso continúa |
ii_rate, ii_pitch, ii_volume, is_lang, is_voice | cambiados durante una lectura, tomados solo en la lectura SIGUIENTE (la documentación prometía la frase siguiente) | propiedades vivas: la frase siguiente las toma |
ue_voice_fallback | mudo cuando solo faltaba la REGIÓN (se pedía fr-CA, voz fr-FR) | levantado; las etiquetas de idioma se comparan sin mayúsculas, y fr_FR vale fr-FR |
destroy (n_pbt_speechout) | durante una lectura, levantaba ue_stopped — sobre una ventana ya medio destruida | ningún evento; of_close() sigue levantando ue_stopped |
of_sentence_count, of_count | la cuenta del texto leído la vez ANTERIOR (0 antes de cualquier lectura) | la cuenta de is_text tal como está |
of_pause, of_resume, of_clear_queue | creaban la voz cuando no existía; una pausa durante la espera de las voces se perdía | 0 sin crear nada, -4 si la página no respondió; la pausa se mantiene hasta of_resume |
ue_sentence (al_index) | una abreviatura (Mr., Dr., e.g.), una dirección deletreada o una versión cortaban la frase; un texto sin punto era una sola frase | ya no cortan; una frase de más de 250 caracteres se corta: los números de frase pueden cambiar |
[pause=ms] | ignorada al FINAL del texto | un silencio tras la última frase, antes de ue_stopped |
La receta: (1) pruebe el -5 de of_speak_from cuando el índice viene de un clic; (2) si su ue_stopped hacía algo al cerrar la ventana, llame usted mismo a of_close() en su close — la destrucción ya no lo levanta; (3) no guarde números de frase de una versión a otra: tómelos de ue_sentence, como la documentación siempre dijo.
11.11 statictext y texto enriquecido: un marcado leído de izquierda a derecha #
La revisión de statictext corrigió sobre todo el texto con etiquetas común (4.7): estas líneas valen para la etiqueta de todo componente — título de pestaña, panel de barra de estado, toast, cuadro de diálogo. No se retira ningún método. Añadido: of_refresh_parent_color(), y las zonas [action], [hyperlink] y los encabezados de [foldarea] de un statictext se alcanzan con el teclado (Tab, luego Intro o Espacio).
| Qué | 3.0 | 4.0 |
|---|---|---|
of_escape_markup (une donnée collée à une balise) | un ] escapado justo después de una etiqueta formaba pareja con el ] de la etiqueta: "[b]" + of_escape_markup("] fin") + "[/b]" mostraba [b]] fin sin negrita | el dato se muestra tal cual, y el formato a su alrededor se mantiene |
[/b], [/i]… | una etiqueta de cierre cerraba la ÚLTIMA etiqueta abierta, fuera cual fuera: [b]A[i]B[/b]C dejaba C en negrita; [/br] también cerraba algo | una etiqueta de cierre solo cierra la suya (los estilos abiertos dentro continúan); una etiqueta de cierre sin apertura, o la de una etiqueta autocerrada, se ignora |
[picture=…] | cortada en la primera coma: un data URI (lo que devuelve of_icon()) o un nombre de archivo con coma no se mostraba | las dimensiones se leen al final del valor: ambos se muestran |
[picture=\\server\share\x.png] | la imagen se leía en el recurso compartido de red | rechazada: una imagen del marcado nunca va a buscar un recurso compartido de red. Pase por of_icon(), que la incrusta, o por un picture |
[hyperlink=HTTPS://…] | un esquema en mayúsculas no se abría | abierto: el esquema se lee sin distinguir mayúsculas; la lista sigue siendo http, https, mailto |
ii_padding | 0 = el margen del tema: una etiqueta nunca podía alinearse con el borde de un campo vecino | -1 = el margen del tema (nuevo predeterminado), 0 = ningún margen |
ue_clicked, ue_double_clicked | levantados al plegar un [foldarea] (tres eventos para un doble clic), y al soltar el ratón tras una selección de texto | ya no se levantan: plegar es cosa de lectura |
[foldarea:… [action=…]] | levantaba ue_action Y plegaba el bloque | solo ue_action, el bloque queda como está |
ii_line_spacing, is_marquee_direction | un valor fuera de 50-400 se ignoraba y se releía 0; un sentido desconocido ("UP") se desplazaba hacia el inicio pero se releía tal cual | llevado al límite más cercano, releído tal como se aplicó; un sentido desconocido vale y se relee MARQUEE_START |
of_scroll_to_end | no devolvía nada | devuelve 0, o -2 si el componente no está creado |
La receta: (1) un ii_padding = 0 escrito para decir «el margen del tema» pasa a -1 — o desaparece; (2) una imagen del marcado alojada en un recurso compartido de red pasa por of_icon(); (3) un ue_clicked que reaccionaba al plegado de una sección ya no se llamará: el plegado no se programa.
11.12 breadcrumb: qué cede cuando la ruta no cabe, y salir del campo #
La segunda revisión de la ruta de navegación (breadcrumb) corrigió el plegado por anchura, el modo OVERFLOW_SHRINK y lo que un segmento se lleva consigo. No se retira ningún método. Una línea vale para todos los menús nativos de la biblioteca: la búsqueda tecleando.
| Qué | 3.0 | 4.0 |
|---|---|---|
of_edit() sans / without ib_editable | devuelve 0, no se abre nada | devuelve -4 |
| salir del campo de la ruta hacia otro control de la aplicación | el campo quedaba abierto, el texto a medio escribir, y no se informaba de nada | un texto modificado se valida: ue_path_entered; pasar a otra aplicación sigue conservando lo escrito |
OVERFLOW_SHRINK | toda etiqueta limitada a 12 caracteres, incluso con sitio; el lugar actual cortado en seco en el borde de la barra | no se corta nada mientras haya sitio; el centro cede primero, hasta una letra y …, el primer segmento y el lugar actual los últimos |
OVERFLOW_COLLAPSE | la etiqueta del lugar actual podía recortarse antes de que el centro se plegara | el centro se pliega primero; el lugar actual solo se recorta como último recurso |
of_insert_item(…, ai_index) | los handles de los segmentos desplazados designaban otro segmento, sus colores y tooltips se quedaban en la dirección antigua | sus handles se liberan, sus colores y tooltips los siguen; un segmento eliminado (of_clear, of_truncate, of_remove_item) se lleva los suyos |
of_item("b").il_back_color / of_item("a/b").il_back_color | dos valores para el mismo segmento | uno solo: la clave única y la dirección designan el mismo segmento |
OVERFLOW_SCROLL, rueda del ratón | sin efecto | desliza la tira |
| los menús nativos (ramas de la ruta, cinta, barra de menús, barra de herramientas, menú contextual): una letra tecleada | no hacía nada, salvo mnemónicos | las letras tecleadas en el mismo segundo saltan a la primera entrada que empieza así («Win»), como en el explorador; un menú de más de 500 entradas solo dibuja lo que se ve |
La receta: (1) un of_edit() cuyo 0 comprobaba sin haber puesto ib_editable devuelve ahora -4; (2) un ue_path_entered puede llegar cuando el usuario hace clic en otro sitio de la ventana — es el mismo texto que con Intro; (3) tras un of_insert_item en medio de la ruta, vuelva a pedir los handles de los segmentos que siguen.
11.13 buttonbar y teclado de la ventana: la tecla va al componente del foco #
La segunda revisión de la barra de botones (buttonbar) tocó el teclado de la ventana, que comparten todos los componentes que responden a Intro, Esc, una letra Alt o un atajo registrado (button, buttonbar y los atajos de of_register_shortcut de todos los componentes). No se retira ningún método; se añade uno, of_focus_button.
| Qué | 3.0 | 4.0 |
|---|---|---|
| dos componentes de una misma ventana responden a Intro, Esc o al mismo atajo (dos hojas MDI, dos paneles) | la recibía el primero creado, incluso en la otra hoja | el más cercano al foco (misma hoja, mismo panel); en empate, ninguno: la tecla se queda en la ventana |
Intro en un CommandButton PB con el foco, o en una DropDownListBox abierta; Esc en una lista abierta | activaba el botón predeterminado / cancelar del componente | se queda en el control, como en un cuadro de diálogo de Windows |
of_register_shortcut(chord, key) en un botón de la barra, luego el botón deshabilitado y rehabilitado, o su letra is_shortcut cambiada | el atajo se borraba | se conserva: la letra Alt y el atajo de la aplicación ya no se tocan |
| la letra Alt de un botón frente a un atajo explícito de la misma tecla | ganaba el primero registrado | gana el atajo explícito; las letras solo se consultan después |
of_clear_shortcuts() | borraba también el is_shortcut de un button | solo borra los atajos de la aplicación |
buttonbar: of_register_shortcut(chord) sin clave, o con una clave que no nombra ningún botón | devolvía 0 y se tragaba la tecla sin activar nada | devuelve -5 |
buttonbar: quitar el botón de is_default / is_cancel, o vaciar la barra | la propiedad conservaba la clave; un botón vuelto a añadir con esa clave volvía a ser el predeterminado | vaciada: el rol se va con su botón |
| buttonbar: otro botón de la barra tiene el foco | el acento se quedaba en is_default mientras Intro activaba el botón enfocado | el botón enfocado lleva el aspecto predeterminado; is_default releído no cambia |
buttonbar: un ii_padding_x negativo | volvía a 12 | la sangría del tema, como ii_padding_y e ii_gap |
| buttonbar: la línea separadora entre botones | dibujada en medio del espacio, aunque los botones no se tocaran | solo entre botones juntos (ii_gap = 0), y del lado correcto en lectura de derecha a izquierda |
La receta: (1) en una barra, dé un atajo a UN botón — of_register_shortcut("Ctrl+S", "save") — la forma sin clave devuelve -5; (2) tras quitar y volver a añadir el botón predeterminado, vuelva a poner is_default; (3) para poner el foco en un botón concreto («No» en lugar de «Sí»), llame a of_focus_button("no").
11.14 button: un botón atenuado suelta la tecla, y Alt+letra responde #
La revisión del botón (button, obsoleto en favor de buttonbar pero todavía entregado y corregido) cambia lo que hace con el teclado y lo que releen sus propiedades. No se retira ni se añade ningún método.
| Qué | 3.0 | 4.0 |
|---|---|---|
un botón atenuado con ib_default, ib_cancel o is_shortcut (ib_enabled = false) | retenía Intro, Esc o su atajo en toda la ventana: la tecla se perdía | devuelve la tecla a la ventana (un CommandButton PB predeterminado, un SingleLineEdit la reciben); reactivado, la recupera |
el & de is_text (&Guardar) | subrayaba la letra, nada más | Alt + la letra activa el botón esté donde esté el foco en la ventana, después de los atajos explícitos, nunca atenuado; una barra de menús PB que usa la misma letra la pierde |
| Intro o Espacio mantenidos sobre el botón con el foco | una ráfaga de ue_clicked; Espacio se disparaba al pulsar | un solo ue_clicked; Espacio se dispara al soltar, como en Windows |
un is_shortcut que la ventana no sabe activar ("Win+S", "Ctrl+Entrée", "Ctrl+Pause") | se releía tal cual; "Win+S" disparaba el botón con una S sola | rechazado: se relee "". Los nombres de teclas se escriben en inglés (Enter, Esc, Del) |
is_shortcut con un tooltip | el atajo no aparecía en ninguna parte | añadido al tooltip (al título de uno enriquecido): «Guardar (Ctrl+S)» |
of_register_shortcut("F5") en el botón, luego ib_default, ib_cancel o is_shortcut cambiados | F5 se borraba | F5 e is_shortcut responden los dos |
is_text, is_image, is_style, ii_badge, il_badge_color, ii_badge_size, ii_image_size leídos tras of_reset() | el valor anterior, aunque la pantalla estaba restablecida | su valor predeterminado ("", "standard", 0, -1) |
| un botón atenuado y la tecla Tab | recibía el foco | se salta |
una etiqueta hecha solo de una imagen ([picture=…]), o un & dentro de una etiqueta | la etiqueta desaparecía, o la etiqueta de marcado se rompía | mostrados tal como se escriben |
La receta: escriba los nombres de teclas de is_shortcut en inglés ("Ctrl+Enter") y relea la propiedad en caso de duda: "" significa rechazado. Si atenuaba un botón predeterminado para «desactivar Intro», nada que cambiar: es lo que ocurre ahora.
11.15 menubar: una barra de menús que devuelve el foco, y Alt+letra desde cualquier sitio #
La revisión de la barra de menús (menubar) cambia lo que hace con el foco y el teclado, y lo que hacen todos los menús nativos en escritura de derecha a izquierda. Ningún método se retira ni cambia de firma.
| Qué | 3.0 | 4.0 |
|---|---|---|
un clic en la barra y luego una elección (Edición > Pegar) | la barra se quedaba con el foco: GetFocus() en ue_item_selected ya no nombraba el campo, el pegado se perdía | el foco vuelve al control anterior antes de que se lance ue_item_selected; igual con Esc y con un clic fuera |
Alt+F mantenido, o F10, con el foco en un campo | nada: solo Alt pulsado y luego F abría File | Alt+F abre File desde cualquier control, F10 hace lo que Alt; un Alt+F registrado por su aplicación conserva la prioridad, y un menú PowerBuilder nativo con la misma letra la pierde |
Ctrl++, Ctrl+-, Ctrl+,, Alt+Left, Ctrl+Space, Ctrl+Tab, Ctrl+Backspace (todos los componentes con atajos) | mostrados, pero nada se disparaba desde un control PB; el button los rechazaba ("") | disparados esté donde esté el foco; estas teclas exigen Ctrl o Alt (solas, siguen siendo navegación) |
en RTL, los desplegables de todos los menús nativos (barra de menús, cinta, barra de herramientas, ruta de navegación, menú contextual…) | alineados a la izquierda, cascadas a la derecha, chevrón ›, Izquierda cerraba | el desplegable cuelga alineado a la derecha bajo su título, una cascada se abre a la izquierda (chevrón ‹): Izquierda abre, Derecha cierra |
| un clic en el título de un menú ya abierto | lo cerraba y luego lo volvía a abrir (un segundo ue_menu_opening) | lo cierra, como en Windows |
un is_shortcut puesto en una entrada que abre una cascada | mostrado, y lanzaba ue_item_selected("file/export"), una dirección que ningún clic produce | ni mostrado ni disparado: un atajo va en una hoja |
| una barra cuyos menús están todos ocultos o atenuados, junto a una cinta | tomaba Alt para nada: los keytips de la cinta ya no salían | devuelve Alt a la cinta mientras ningún título sea alcanzable |
dos títulos con la misma letra (&Archivo, &Favoritos con la misma letra) | Alt+F abría siempre el primero | en la barra, la banda pasa de uno a otro, Intro abre |
| una entrada quitada y vuelta a añadir en la misma dirección (una lista de archivos recientes) | recuperaba los colores y la información sobre herramientas de la desaparecida | nace sin color (il_back_color se relee -1); igual tras of_remove_menu y of_clear |
una letra acentuada o no latina en un desplegable (&Édition) | subrayada, pero ninguna tecla la alcanzaba | se teclea como en el teclado |
| la tecla Tab en la barra | una parada por título | una sola parada para toda la barra; las flechas pasan de un título a otro |
| nuevo | — | is_image (el icono de una entrada, cambiado en caliente), is_group (entradas de opción), ue_menu_closed(as_key) (un desplegable cerrado sin elección) |
La receta: en ue_item_selected, trabaje sobre GetFocus() o sobre el DataWindow en edición como con un menú PowerBuilder; ponga un & en cada título; si su aplicación usaba Alt+letra para otra cosa, regístrelo (of_register_shortcut): conserva la prioridad.
11.16 ribbon: una elección que se mantiene, menús que siguen abiertos, rechazos que se dicen #
La segunda revisión de la cinta (ribbon) corrige lo que veía el usuario — una elección de fuente deshecha, un menú que se cerraba bajo el puntero — y aplica a la cinta la regla de la biblioteca: una adición o una retirada imposible devuelve -5. Ningún método se retira ni cambia de firma.
| Qué | 3.0 | 4.0 |
|---|---|---|
| ribbon: una opción en la lista de un cuadro combinado y luego la salida del campo | ue_combo_changed dos veces — la opción y luego el texto tecleado antes, que la sobrescribía; is_text releía el texto tecleado | un solo ue_combo_changed, la opción; el campo la muestra enseguida, incluso en un cuadro no editable |
ribbon: Intro o Escape en un cuadro combinado, un contador, o sobre un botón de la cinta con el foco | iban también al botón predeterminado o de cancelación de la ventana | se quedan en la cinta; Escape en un campo devuelve el texto anterior a lo tecleado |
| ribbon: un menú abierto mientras la aplicación actualiza la cinta (otro control ocultado, una pestaña renombrada, un mosaico añadido) | se cerraba, y la elección hecha en él no activaba ningún evento | sigue abierto; solo se cierra si su propio control desaparece, se oculta o se atenúa |
ribbon: is_app_button reescrito («File» → «Archivo»); is_app_button = "" | el menú de aplicación se vaciaba; una cadena vacía mostraba «File» | el menú se queda; una cadena vacía retira el botón y su menú |
ribbon: of_select_tab de una pestaña desconocida u oculta | 0 | -5, nada cambia |
ribbon: añadir una entrada de menú, un mosaico, una opción de cuadro combinado, un botón de acceso rápido, una entrada del menú de aplicación o un grupo contextual con una clave ya tomada; una clave con una barra vertical o que empieza por __; un padre desconocido (separador, encabezado, opción, entrada sin botón de aplicación, pestaña bajo un grupo contextual ausente) | 0: un duplicado, o nada en absoluto (una pestaña contextual sin banda) | -5 |
ribbon: of_remove_tab, of_remove_group, of_remove_item, of_select_gallery_item en una dirección desconocida; of_open en un botón de acceso rápido | 0, sin efecto | -5. of_open("pestaña/grupo") abre el panel de un grupo replegado |
ribbon: un handle of_qat_item conservado tras of_remove_item de su botón | seguía válido y designaba un botón desaparecido | liberado: IsValid() devuelve false |
| ribbon: teclas de acceso rápido — dos controles con la misma letra, uno de ellos oculto; un control de un grupo replegado | el oculto robaba la letra y no se ejecutaba nada; la letra de un grupo replegado no hacía nada; una tecla de dos letras no respondía nunca | solo responde un control visible y activo; la letra abre el panel del grupo replegado; "FP" se teclea letra a letra |
| ribbon: flechas del teclado sobre un botón dividido, un selector de color, un cuadro combinado o una galería atenuados | se detenían en ellos, sin poder hacer nada | los saltan; Abajo sobre un botón desplegable o dividido abre su menú |
La receta: (1) ue_selection_changed y ue_minimized se lanzan también cuando su código llama a of_select_tab o asigna ib_minimized, como con un clic: si llamaba usted mismo a su tratamiento justo detrás, quite esa llamada, se ejecutaría dos veces; (2) compruebe el -5 de las adiciones si su construcción puede repetir una clave; (3) un is_app_button = "" que quería decir «File» se escribe ahora is_app_button = "File". Las retiradas bajo un control y el handle de una entrada del menú de aplicación están en la tabla 11.5.
11.17 toolbar: una regla de atajos, un único tooltip, rechazos que se dicen #
La segunda revisión de la barra de herramientas (toolbar) alinea sus atajos con los de la ventana y hace visible lo que se escribía sin efecto. No se retira ningún método ni cambia ninguna firma; aparecen dos códigos de retorno (-5).
| Qué | 3.0 | 4.0 |
|---|---|---|
una herramienta con is_shortcut = "Delete" (una tecla sola) y un campo en la barra | Supr en el campo disparaba la herramienta y no borraba nada; fuera de la barra, nada | una tecla sola nunca es un atajo: hace falta Ctrl o Alt, o de F1 a F24 — la regla de la ventana |
| una tecla de atajo mantenida (todos los componentes) | la acción se repetía: un conmutador parpadeaba | una acción por pulsación |
of_register_shortcut(acorde, clave) heredado, en una toolbar | no disparaba nada, y la tecla se perdía | toma la dirección de la herramienta o de la entrada ("main/save"), -5 si no; la forma de un argumento devuelve -5; of_clear_shortcuts también vacía is_shortcut |
| el atajo de un campo, de un selector de fecha, de una etiqueta | se tragaba la tecla sin hacer nada | el campo recibe el cursor (Ctrl+F en una búsqueda), el selector abre su calendario; una etiqueta no admite ninguno |
el tooltip dado al añadir (of_add_button(…, tooltip)) | etiquetas mostradas tal cual; dos burbujas a la vez si luego se ponía is_tooltip | un único tooltip con tema, texto enriquecido, el atajo al final; is_tooltip lo sustituye y se relee |
of_add_bar(MAIN_BAR) tras una primera herramienta en main | devolvía 0 sin hacer nada | -5: para colocarla, añada main antes de cualquier herramienta |
of_set_layout de un texto que no es JSON | devolvía 0 | -5 |
ue_layout_changed | ausente cuando añadir una barra desplazaba a otra; disparado al mostrar una barra ya visible | se dispara en el primer caso, ya no en el segundo |
| separadores, un desplegable o cascada cuyas entradas están todas ocultas | trazos huérfanos; el desplegable y la cascada seguían activos y mudos | ningún trazo al principio, al final ni duplicado; se dibujan atenuados |
| un clic derecho en la barra | ningún evento, ue_rclicked incluido | ue_item_rclicked(dirección) en una herramienta, ue_rclicked fuera de toda herramienta — nunca ambos |
los colores de una entrada de menú (of_menu_item(…).il_back_color) | escritos y releídos, nunca dibujados | dibujados en el menú |
toolbar: ue_rclicked, ue_mouse_enter, ue_mouse_leave | documentados, pero nunca lanzados | lanzados: un clic derecho en la barra fuera de toda herramienta, la entrada y la salida del ratón |
La receta: (1) un atajo de herramienta sobre una tecla sola (salvo F1 a F24) lleva Ctrl o Alt; (2) en una toolbar, of_register_shortcut recibe la dirección de la herramienta o de la entrada; (3) para colocar la barra main, llame a of_add_bar(MAIN_BAR, banda, rango) antes de su primera herramienta.
11.18 statusbar: un asa que redimensiona, un mensaje que no sustituye el texto #
La segunda revisión de la barra de estado (statusbar) da al asa de redimensionamiento su verdadero papel y convierte cada clave vacía o desconocida en un rechazo. Ningún método se retira ni cambia de firma; of_flash_panel gana un código de retorno (-5).
| Qué | 3.0 | 4.0 |
|---|---|---|
ib_show_resize_grip = true, luego arrastrar el asa | un dibujo: nada se mueve | la ventana se redimensiona desde su esquina (inferior izquierda en RTL); el asa no se dibuja en una ventana maximizada o sin borde redimensionable |
of_flash_panel sobre una clave que la barra nunca recibió, o sobre una dirección | 0, no se muestra nada | -5 |
of_panel(k).is_text releído durante un of_flash_panel | el mensaje | el texto del panel (el mensaje pasa por encima) |
of_panel(""), of_panel("a/b"): escritura y relectura | escribía en los paneles sin clave y en los separadores | no designa nada: no escribe en ninguna parte, se relee vacío |
is_state fuera de las constantes STATE_* ("Error", "very bad") | releído tal cual, sin color; un valor con un espacio provocaba un error de script | se ignoran las mayúsculas ("Error" = error); cualquier otro valor equivale a « ningún estado » y se relee vacío |
ii_progress = 150 y luego releído | 150 (dibujado al 100 %) | 100, lo que se dibuja |
| un panel desactivado que lleva un estado | conservaba el color del estado: parecía activo | atenuado, marca de estado incluida |
un panel retirado, vaciado (of_clear, of_clear_menu), desactivado u ocultado mientras su lista está abierta | la lista seguía abierta, y una elección lanzaba ue_panel_menu_clicked | la lista se cierra, no se lanza nada (lo mismo para una entrada que pasó a atenuada u oculta) |
| doble clic en un panel con lista desplegable | ue_panel_double_clicked además de la lista abierta | solo la lista |
| Flecha arriba / abajo en un panel con lista desplegable que tiene el foco | pasaba al panel vecino | abre la lista (las flechas Izquierda / Derecha siguen desplazando) |
| una lista desplegable hecha solo de separadores | flecha y lista vacía | sin lista: el panel sigue siendo lo que dice ib_clickable |
La receta: (1) establezca ib_show_resize_grip en una barra de estado situada en la parte inferior de una ventana redimensionable; (2) compruebe el retorno de of_flash_panel como el de los demás métodos con clave; (3) dé una clave a cada panel que quiera releer o modificar.
11.19 dockcontainer: un of_select_panel que encuentra el panel, ventanas flotantes que recuerdan #
La segunda revisión del contenedor de paneles (dockcontainer) da a of_select_panel un efecto sobre un panel replegado o flotante, y hace que la disposición recuerde las ventanas flotantes. Ningún método se retira ni cambia de firma.
| Qué | 3.0 | 4.0 |
|---|---|---|
of_select_panel de un panel replegado, flotante u oculto | 0, no se ve nada | replegado: su panel deslizante se abre; flotante: su ventana pasa al frente; oculto: -5 |
of_float_panel del panel principal o de un panel oculto | 0, no pasa nada | -5 |
of_move_panel que apila el panel PRINCIPAL | 0, y su vecino desaparece sin pestaña | -5, nada se mueve |
as_position desconocida (of_add_panel, of_move_panel) | 0, el panel se acopla abajo | -5, el control no se toca |
disposición (of_get_layout, ue_layout_changed, of_set_layout) y paneles FLOTANTES | olvidados: un panel desacoplado volvía acoplado; mover su ventana no lanzaba nada | conservados con el lugar de su ventana, reabiertos al restaurar (devueltos a una pantalla); mover o redimensionar la ventana lanza ue_layout_changed |
of_set_layout de una disposición sin panel principal | el panel principal actual se quita | se conserva |
is_title de un panel añadido sin título | su clave; un título dado después al panel principal no hacía aparecer su cabecera | "" (la pestaña sigue mostrando la clave); la cabecera del principal sigue su título |
La receta: (1) ue_panel_selected, ue_panel_floated y ue_panel_pinned se lanzan también tras of_select_panel, of_float_panel e ib_pinned, como con un gesto: si llamaba usted mismo a su tratamiento justo detrás, quite esa llamada, se ejecutaría dos veces; (2) compruebe el -5 de of_move_panel, of_float_panel y of_select_panel; (3) una disposición guardada antes de la 4.0 se restaura tal cual, sus paneles siguen acoplados.
11.20 tab: códigos de retorno honestos, un clic derecho que vuelve a la aplicación #
La segunda revisión de las pestañas (tab) hace que of_add_page y of_select_page digan lo que rechazan, anuncia la disposición cuando su código oculta una pestaña, y devuelve el clic derecho a la aplicación cuando el menú integrado está desactivado. Ningún método se retira ni cambia de firma; se añade un evento.
| Qué | 3.0 | 4.0 |
|---|---|---|
of_add_page de un control ya alojado (por esta pestaña con otra clave, o por otro componente) | 0, y una pestaña sin nada detrás | -5, no se añade nada |
of_select_page de una pestaña rechazada cuando ninguna está activa (todas ocultas) | 0 | -5 |
Ocultar o volver a mostrar una pestaña desde su código (ib_visible) | ningún ue_layout_changed | ue_layout_changed lanzado con la disposición entera, como para of_move_page: es el aviso de que la disposición que hay que guardar ha cambiado |
Clic derecho en una pestaña con ib_context_menu = false | nada | la pestaña se selecciona y luego se lanza ue_tab_rclicked(as_key) (nuevo): abra ahí su menú |
Volver a la pestaña actual mientras ue_selection_changing espera su respuesta | una respuesta «sí» activaba igualmente la otra pestaña | la pregunta se anula: gana el último gesto del usuario |
Una pestaña desactivada u oculta mientras ue_tab_closing espera su respuesta | se cerraba igualmente | permanece abierta |
| Cerrar con el teclado (Supr) la pestaña que tiene el foco | el foco salía de la barra, las flechas ya no respondían | el foco pasa a la pestaña que se ha vuelto activa |
La receta: (1) compruebe el -5 de of_add_page (una página se aloja una sola vez) y de of_select_page; (2) si guarda la disposición en ue_layout_changed, nada que hacer — ahora sigue también sus ib_visible; (3) donde ib_context_menu = false dejaba el clic derecho sin respuesta, programe ue_tab_rclicked.
11.21 listbar: una entrada oculta nunca seleccionada, una disposición comprobada #
La segunda revisión de la barra de navegación (listbar) hace que of_select_item rechace una entrada oculta y of_set_layout un texto que no es una disposición, y añade una insignia de contador, las teclas Izquierda / Derecha y el clic derecho en una entrada. Ningún método se retira ni cambia de firma.
| Qué | 3.0 | 4.0 |
|---|---|---|
of_select_item de una entrada oculta (ib_visible = false) | 0, una selección invisible | -5, la selección no se mueve; ocultar la entrada ya seleccionada la mantiene seleccionada |
of_set_layout de un texto que no es un objeto JSON (archivo truncado) | 0, no se aplica nada | -5, no se envía nada |
| Sección plegada en una disposición restaurada, añadida más tarde | nace plegada | nace desplegada: el plegado solo vale para una sección presente |
| Sección con título vacío | sus entradas pegadas a las de arriba; plegable, sin nada para volver a abrirla | un espacio (una línea fina en el riel) la separa; nunca se pliega |
Nuevo: is_badge (entrada), ue_item_rclicked(as_keys), Izquierda / Derecha y salto por letra | — | una insignia de contador, el clic derecho en una entrada (un solo evento, la entrada no se selecciona), el plegado con el teclado |
La receta: (1) ue_selection_changed se lanza también tras un of_select_item de su código (abrir la página de inicio al arrancar, por ejemplo), como con un clic: si abría usted mismo la página justo detrás, déjeselo al evento, si no se abriría dos veces; (2) compruebe el -5 de of_select_item si oculta entradas, y el de of_set_layout si la disposición viene de un archivo; (3) una sección sin título ya no se pliega: dele un título si debe plegarse.
11.22 tilesbox: un título que ya no crea grupos, la reorganización desde el teclado #
La segunda revisión del panel de mosaicos (tilesbox) impide que el título de un grupo cree ese grupo, hace que of_add_live_item rechace un texto vacío, da una sola parada de tabulación por grupo y añade la reorganización desde el teclado. Ningún método se retira ni cambia de firma. il_badge_color sigue ahora la regla de todos los colores (vea la fila picture, button, tilesbox más arriba).
| Qué | 3.0 | 4.0 | |
|---|---|---|---|
of_group(clave).is_title sobre un grupo nunca añadido | creaba un grupo visible que no se podía ni llenar ni quitar (salvo con of_clear) | no escribe nada: solo of_add_group crea un grupo; una clave vacía, o con / o ` | `, da un handle inerte |
of_add_live_item con un texto vacío | 0, y el texto fijo is_live_text se borraba | -5, nada cambia | |
of_tile(dirección).of_count() | 0 | el número de caras puestas por of_add_live_item | |
Reorganizar (ib_reorderable = true) | solo con el ratón | también desde el teclado: Ctrl+Izquierda/Derecha en el grupo (sentido de lectura), Ctrl+Arriba/Abajo hacia el grupo vecino; mismos ue_tile_moved y ue_layout_changed | |
| Tecla Tab | se detenía en cada mosaico; una flecha hacia un borde sin vecino saltaba al mosaico anterior o siguiente del documento | una parada por grupo (el último mosaico con foco), las flechas recorren los mosaicos; en el borde, el foco se queda | |
| Mosaico desactivado u oculto mientras se arrastra | movido al soltar, ue_tile_moved disparado | el arrastre se abandona, nada se mueve | |
ib_collapsed de un grupo asignado por su código | ningún evento | ue_group_toggled, como un clic en el encabezado (nada cuando el grupo ya está en ese estado) |
La receta: (1) cree un grupo con of_add_group antes de cambiar su título; (2) compruebe el -5 de of_add_live_item si el texto de una cara puede estar vacío; (3) lo que hacía en ue_tile_moved vale ahora también para un movimiento desde el teclado.
11.23 stepbar: «todo terminado» anclado en el último paso, pasos retirados que ya no dejan nada #
La segunda revisión de la barra de pasos (stepbar) hace que «todo terminado» sea coherente con la regla del paso en curso (una identidad, no un rango), hace que un paso retirado se lleve lo que le pertenecía y corrige cuatro defectos de visualización. Ninguna firma cambia.
| Qué | 3.0 | 4.0 |
|---|---|---|
of_add_step mientras la barra está en «todo terminado» | el paso añadido aparecía terminado (marca); ii_current pasaba a n + 2 sin evento | añadido después del último paso de entonces, pasa a ser el paso en curso y se lanza ue_step_changed; insertado antes, nada cambia |
of_step(clave).is_state sobre una clave que no nombra ningún paso | guardado y aplicado después al paso añadido con ese nombre | ignorado; un paso añadido nace con su estado calculado |
Colores e información sobre herramientas de un paso retirado (of_remove_step, of_clear_steps) | volvían en el siguiente paso añadido con la misma clave | se van con el paso |
Barra compactada (OVERFLOW_AUTO) cuyo paso en curso está forzado a error | ninguna etiqueta; un paso forzado a STATE_CURRENT ganaba la suya | la etiqueta se queda en el paso en curso (el rango de ii_current), sea cual sea su estado |
OVERFLOW_SHRINK | etiquetas en varias líneas, palabras largas cortadas sin «…» | una línea por etiqueta, terminada en «…» |
POSITION_BOTTOM con una segunda línea (is_description) | encima de la etiqueta, pegada a ella | bajo la etiqueta, encima del círculo |
Pasar el ratón sobre un paso desactivado o fuera de alcance (il_back_color_hover, il_text_color_hover) | el color al pasar se encendía | nada se enciende: el paso no responderá al clic |
La receta: si su código añade un paso tras poner «todo terminado» (ii_current = of_count() + 1) y quiere que la barra siga así, vuelva a poner ii_current = of_count() + 1 después del añadido. Si reconstruye la barra con las mismas claves, vuelva a poner los colores e informaciones sobre herramientas deseados: ya no sobreviven a la retirada.
11.24 progressbar: una barra que cabe en su control, una etiqueta que sigue el idioma #
La segunda revisión de la barra de progreso (progressbar) hace que quepa donde estaba una HProgressBar, solo envía al componente los valores que cambian el dibujo, escribe el porcentaje en el idioma de visualización y añade is_label_format (una etiqueta propia) e is_state (pausa, error). Ninguna firma cambia.
| Qué | 3.0 | 4.0 |
|---|---|---|
| Barra lineal en un control bajo o ancho | margen de 12 px, 640 px de ancho como máximo: por debajo de 24 px de alto aparecía una barra de desplazamiento y la barra quedaba cortada | margen de 2 px, todo el ancho; la pista (14 px) y la etiqueta se reducen con la altura |
Etiqueta del porcentaje (ib_label) | «50%» en todos los idiomas | en el idioma de visualización: «50 %» en francés |
of_set_property("value", "12,5"), o un texto que no es un número | enviado tal cual: la orden se perdía sin decir nada; un minimum no válido bloqueaba la escala hasta el siguiente of_reset | «12,5» se lee 12.5; un texto que no es un número (ni true/false para un booleano) se ignora |
id_value releído | el valor que el componente había recibido; cada asignación era un viaje de ida y vuelta con él | el último valor asignado por la aplicación; el componente solo recibe los que cambian el dibujo (una décima de punto porcentual) |
of_reset() sobre una barra llena | la barra bajaba a cero deslizándose | vuelve a cero de golpe |
La receta: (1) si calculaba un porcentaje a mano, asigne id_maximum y el valor bruto, en cada fila si lo desea; (2) si una prueba automática leía la etiqueta, espere el formato del idioma de visualización; (3) para mostrar una pausa o un error, prefiera is_state a un color elegido con il_color.
11.25 picture: una asignación relee el fichero, un fallo dice su motivo #
La segunda revisión de la imagen (picture) le hace releer el disco en cada asignación, como el Picture nativo, da a ue_error un segundo argumento que dice por qué falta la imagen, rechaza una dirección http:// en lugar de fallar sin motivo y solo lanza un ue_clicked en un doble clic. ue_error es la única firma que cambia.
| Qué | 3.0 | 4.0 |
|---|---|---|
| picture: doble clic en la imagen | dos ue_clicked y luego ue_double_clicked | un solo ue_clicked y luego ue_double_clicked — como el Picture nativo |
picture: ue_error | ue_error (string as_message): solo el mensaje | ue_error (string as_message, string as_reason): as_reason es una de las constantes REASON_* (no encontrado, demasiado grande, http rechazado, formato no mostrado, otro). Su script del evento recibe el argumento por sí solo; una llamada event ue_error(…) escrita en su código pasa ahora dos |
picture: is_source = "http://…" | intentado en https por el motor; en un servidor sin TLS, un fallo sin motivo | rechazado de entrada: glifo de error y ue_error con REASON_INSECURE. Descargue antes la imagen (n_pbt_restclient.of_download) y muestre el fichero |
picture: la misma ruta asignada de nuevo tras reescribir el fichero; mono: o tint: en is_source; una imagen .avif | la imagen anterior; el glifo de error; no encontrada | el fichero releído, como el Picture nativo; el glifo recoloreado por el tema; mostrada |
| picture: imagen ampliada más pequeña que su marco (bandas); rueda horizontal, panel táctil; botón derecho mantenido | salía del marco al arrastrarla; un paso de zoom por evento, alejamiento en un desplazamiento horizontal; la imagen se movía | se queda donde la colocan is_align e is_valign; zoom proporcional al gesto, nada en un desplazamiento horizontal; solo el botón izquierdo desplaza |
La receta: (1) si un script abría una ficha en ue_clicked y otra acción en ue_double_clicked, nada que hacer — el doble clic ya no vuelve a abrir la ficha; (2) sustituya cualquier análisis del texto de as_message por una prueba de as_reason; (3) una imagen de un servidor de intranet en http se descarga primero (n_pbt_restclient.of_download en una carpeta temporal) y luego se muestra por su ruta; (4) si añadía un ?v= a la ruta para forzar la relectura de una imagen reescrita, quítelo: volver a asignar basta.
11.26 video: subtítulos y formatos que se reproducen, un fallo que dice su motivo #
La segunda revisión del reproductor (video) da a ue_failed un segundo argumento que dice por qué una película no se reproduce, rechaza una dirección http://, reproduce .mkv y .mov, carga los subtítulos dados por URL, y pone la película en pausa cuando el componente se oculta. ue_failed es la única firma que cambia.
| Qué | 3.0 | 4.0 | |
|---|---|---|---|
video: ue_failed | ue_failed (string as_message) | ue_failed (string as_message, string as_reason) — as_reason es una constante REASON_* | |
video: una fuente http:// | fallaba como « no se encuentra » | rechazada enseguida, REASON_INSECURE; solo https se reproduce | |
video: un .mkv, un .mov | .mkv « no se encuentra », .mov rechazado por su nombre | servidos y reproducidos cuando el motor los decodifica; .avi, .wmv, .flv siguen rechazados (REASON_FORMAT) | |
| video: un subtítulo dado por URL | nunca se mostraba, sin decir nada | descargado por el componente (el servidor debe permitir CORS); un rechazo lanza ue_subtitles_failed | |
video: of_add_subtitles dos veces para el mismo idioma | reemplazaba la pista en silencio, 0 | -5 (también para un idioma con / o ` | ); of_remove_subtitles` antes |
| video: el componente oculto durante la reproducción | la película seguía, con sonido | en pausa, ue_paused, y espera of_play (ib_pause_when_hidden, verdadero por defecto) | |
| video: arrastrar el volumen en la barra | un ue_volume_changed por píxel | uno solo, al soltar | |
video: of_capture hacia un .jpg | un PNG con un nombre .jpg | un JPEG; la sobrecarga of_capture(as_path, al_max_width) reduce la imagen | |
video: of_get_last_error tras un fallo de of_capture | el error anterior de la biblioteca | el motivo de ESTE fallo (carpeta ausente, ruta vacía…) | |
video: Escape en pantalla completa, ib_enabled = false | sin efecto (solo Alt+F4 salía) | sale de la pantalla completa |
La receta: (1) añada as_reason a su script de ue_failed y compruébelo en lugar del texto del mensaje; (2) una película o un subtítulo de un servidor de intranet en http se descarga primero (n_pbt_restclient.of_download hacia una carpeta temporal), luego se reproduce por su ruta; (3) ue_fullscreen_changed, ue_pip_changed, ue_source_changed y ue_chapter_changed se lanzan también tras sus propias llamadas (of_fullscreen, of_pip, of_next, of_seek…), como con un gesto: si su código llamaba él mismo a su tratamiento justo detrás, quite esa llamada, se ejecutaría dos veces; (4) un muro de pantallas que debe reproducirse oculto pone ib_pause_when_hidden = false; (5) para reemplazar la pista de un idioma, of_remove_subtitles y luego of_add_subtitles.
11.27 pdfviewer: of_refresh dice lo que hace, nada se descarga sin que lo sepa #
La segunda revisión del visor (pdfviewer) da un código de retorno a of_refresh — y, para que el mismo nombre tenga el mismo sentido, también a los de webbrowser y shellexplorer —, informa de todo enlace del documento y rechaza lo que nunca se mostrará en lugar de dejar esperando. Ninguna firma de evento cambia.
| Qué | 3.0 | 4.0 |
|---|---|---|
pdfviewer · webbrowser · shellexplorer: of_refresh | subroutine | long: 0 una vez solicitado, -4 sin documento o página (pdfviewer, webbrowser), -2 si el componente no está creado |
pdfviewer: of_refresh tras un rechazo (http://…) | no hacía nada, sin evento | el rechazo se repite: ue_load_failed |
pdfviewer: una dirección data: de más de 2 MB de caracteres | nunca mostrada, ningún evento | ue_load_failed con REASON_TOO_LARGE, de inmediato |
| pdfviewer: un PDF remoto enviado «como descarga» | ue_load_failed, pero el fichero acababa en Descargas | ue_load_failed con REASON_REFUSED, no se descarga nada |
pdfviewer: ue_link_clicked en un enlace mailto:, file: o relativo | nunca lanzado (solo http y https); un enlace relativo llegaba como dirección interna | lanzado para todo enlace; as_url es un mailto:… tal cual, o una ruta de disco para un fichero local |
pdfviewer: of_print_to_pdf | escribía la página alrededor del lector, no el documento | -4, no se escribe nada: copie el fichero de is_source |
pdfviewer: file://localhost/C:/…, file:////servidor/recurso/… | una ruta errónea, no encontrada | C:\… y \\servidor\recurso\… |
pdfviewer: el foco dado al componente (Tab, of_focus_webview) | se quedaba en la página: las teclas no desplazaban el documento | pasa al documento: AvPág, flechas, Inicio / Fin lo desplazan |
La receta: (1) una llamada uo_pdf.of_refresh() compila tal cual; compruebe su retorno si quiere saber que no había nada que volver a leer; (2) un documento de más de 1,5 MB en memoria se escribe en un fichero temporal y luego se muestra por su ruta; (3) en ue_link_clicked, espere también un mailto: y una ruta de disco, no solo una dirección web; (4) para copiar el documento mostrado, copie el fichero de is_source en lugar de of_print_to_pdf.
11.28 codeeditor: una búsqueda que se ve, un «ir a» que mueve el cursor, reemplazar #
La segunda revisión del editor de código (codeeditor) lleva a la pantalla lo que encuentra la búsqueda, conserva el cursor del usuario durante una búsqueda, renombra ii_doc_line y añade lo que se espera de un editor de scripts: reemplazar, dirigir el cursor, saber si hay que guardar, mostrar un error en el margen. Ninguna firma de evento cambia.
| Qué | 3.0 | 4.0 |
|---|---|---|
codeeditor: ii_doc_line | un long con el prefijo de integer | il_doc_line: renómbrelo; una línea más allá del final ya no marca nada (se relee tal como se escribió) |
codeeditor: of_find con ib_search_enabled = false | 0, sin buscar nada | -4 |
codeeditor: of_find("") | relanzaba la búsqueda anterior | vacía el campo y borra los resaltados, sin ue_find_result |
codeeditor: of_find, Intro en la barra | la coincidencia quedaba a menudo fuera de la vista; el cursor iba a ella | la coincidencia se lleva a la pantalla; la búsqueda parte del cursor, que solo se mueve al cerrar la barra (Esc) |
codeeditor: of_insert_text con la barra de búsqueda abierta | reemplazaba la coincidencia encontrada, fuera de la vista, y vaciaba el historial de Ctrl+Z | inserta en el cursor del usuario, llevado a la pantalla; Ctrl+Z lo deshace |
codeeditor: un is_text nuevo | conservaba el desplazamiento del documento anterior; un texto solo con CR se releía en LF | se muestra desde su primera línea y borra los marcadores; solo CR se relee en CR (una mezcla en CRLF) |
codeeditor: ib_current_line con ib_wrap | ninguna banda | la banda cubre toda la línea del cursor |
| codeeditor: novedades | — | reemplazar (of_replace, of_replace_all, Ctrl+H) y opciones de búsqueda (ib_find_match_case, ib_find_whole_word, ib_find_regex, F3); cursor y selección (il_caret_line, il_caret_column, of_select_range, of_selected_text, of_line_count); ib_modified; marcadores en el margen (of_add_marker, of_remove_marker, of_clear_markers, of_marker_count) |
La receta: (1) sustituya ii_doc_line por il_doc_line; (2) compruebe el -4 de of_find si la búsqueda puede estar desactivada; (3) para «ir a la línea» de un error moviendo el cursor, asigne il_caret_line (y of_add_marker para mostrarlo) — il_doc_line solo marca; (4) si seguía ue_caret_changed para saber dónde se insertó un fragmento, lea il_caret_line tras of_insert_text; (5) un botón Guardar puede seguir ib_modified, que vuelve a poner en false tras guardar.
11.29 jsontree y xmltree: el teclado se queda en el árbol, una búsqueda que ya no mueve la selección #
La segunda revisión del árbol JSON (jsontree) trató el motor que comparte con el árbol XML (xmltree): lo que sigue vale para ambos, salvo indicación. Ninguna firma cambia; el árbol JSON gana of_expand_path, of_collapse_path, of_is_expanded, of_copy y el evento ue_node_toggled, lanzado por un gesto del usuario como por of_expand_path y of_collapse_path.
| Qué | 3.0 | 4.0 |
|---|---|---|
| jsontree, xmltree: Intro sobre una hoja | pasada a la ventana: su botón por defecto se disparaba | la conserva el árbol, lanza ue_node_clicked como un clic; sin selección, nada |
| jsontree, xmltree: Esc con el cuadro de búsqueda abierto | se disparaba el botón Cancelar de la ventana | cierra primero el cuadro; con el cuadro cerrado, Esc vuelve a la ventana |
jsontree, xmltree: líneas de cierre (}, ], </x>) | alcanzadas por las flechas (ue_selection_changed de un bloque ya anunciado); un clic devolvía la vista a la apertura | saltadas por el teclado; un clic selecciona el bloque sin mover la vista |
| jsontree, xmltree: flecha derecha sobre una hoja | bajaba una línea | no hace nada, como en un árbol |
| jsontree, xmltree: una búsqueda durante una selección | of_search_next o Esc podían plegar la línea elegida y mover la selección sin evento | la línea seleccionada sigue visible y seleccionada: of_selected_path no cambia |
jsontree: el mensaje de ue_error | mitad traducido, mitad inglés («JSON no válido : Unexpected end of JSON (line 1, column 6)») | enteramente en el idioma de visualización, línea y columna incluidas: no compare su texto |
| jsontree, xmltree: un documento de más de 1.500.000 líneas | mostrado, pero su final inalcanzable, sin decir nada | rechazado: un mensaje en el árbol y ue_error |
| jsontree: Ctrl+C en el árbol | no copiaba nada, y seguía como atajo de la ventana | copia la selección (como of_copy); sin selección, el atajo sigue siendo de la ventana |
La receta: (1) si una ventana contaba con Intro en el árbol para disparar su botón por defecto, añada ese paso en ue_node_clicked; (2) no busque más una clave vacía en ue_selection_changed (una línea de cierre ya no se selecciona); (3) no analice el texto de ue_error: sigue el idioma; (4) para restablecer los pliegues de un usuario, lea of_is_expanded y vuelva a ponerlos con of_expand_path / of_collapse_path.
11.30 xmltree: cada línea tiene su XPath, cualquier XPath selecciona #
La segunda revisión del árbol XML (xmltree) retoma todo lo que la sección anterior dice del motor compartido. Añade lo propio del XML: una ruta que designa SIEMPRE el nodo de la línea, legible tal cual por n_pbt_xml. Ninguna firma cambia; el árbol XML gana of_selected_xml, of_copy, of_expand_path, of_collapse_path, of_is_expanded y el evento ue_node_toggled.
| Qué | 3.0 | 4.0 |
|---|---|---|
| xmltree: ruta de una línea de texto, de comentario o de instrucción de procesamiento | la de su elemento (mientras of_selected_value daba su propio texto); "" antes de la raíz | la suya: /r/text()[2], /r/comment()[1], /r/processing-instruction('x')[1], /comment()[1] — n_pbt_xml.of_get_value lee allí el mismo valor |
| xmltree: elemento cuyo prefijo se redeclara con otro URI, o con un hermano del mismo nombre en otro espacio de nombres | una ruta que no encontraba nada, u OTRO nodo | *[local-name()='x' and namespace-uri()='urn:…'], que designa ese nodo exactamente |
xmltree : of_select_path | solo la forma exacta que escribe el árbol, si no -5 | cualquier XPath (//line[@sku='A'], /r/a[1]…): se selecciona el primer nodo encontrado, of_selected_path da luego la ruta del árbol |
xmltree: el mensaje de ue_error | « XML no válido : error on line 1 at column 11: … »; el árbol quedaba vacío | « XML no válido : línea 1, columna 11 (…) », en el idioma de visualización; el árbol muestra el origen, con la línea errónea marcada |
xmltree: <?xml-stylesheet?> sin declaración XML | mostrado dos veces | una sola línea |
n_pbt_xml: un XPath con @xml:lang | rechazado (prefijo xml desconocido), valor vacío | leído: el prefijo xml está ligado de oficio, como exige la norma |
| xmltree: Flecha derecha en una etiqueta con atributos | los atributos solo se alcanzaban con el ratón | recorre los atributos uno a uno (Flecha izquierda vuelve), un ue_selection_changed por paso, luego baja |
| xmltree: Ctrl+C, plegar por ruta | nada; Ctrl+C iba a la ventana como atajo | of_selected_xml, of_copy y Ctrl+C (el XML de la selección), of_expand_path, of_collapse_path, of_is_expanded (cualquier XPath), ue_node_toggled (un gesto, o of_expand_path / of_collapse_path) |
La receta: (1) si subía de una línea de texto o de comentario a su elemento con of_selected_path, tome el padre de la ruta (lo que precede a la última /); (2) no analice el texto de ue_error: sigue el idioma; (3) una ruta guardada en 3.0 bajo espacios de nombres mezclados se recupera pasándola a of_select_path, que ahora acepta cualquier XPath, y releyendo después of_selected_path.
11.31 xml: rechazos que se dicen, espacios de nombres que se mantienen #
La segunda revisión de n_pbt_xml no cambia ninguna firma. Hace que of_remove siga los códigos de la biblioteca, rechaza una lectura mientras un documento construido tenga un elemento abierto, y hace que los espacios de nombres se mantengan: lo que un mutador escribe se relee en el espacio correcto, o se rechaza diciéndolo.
| Qué | 3.0 | 4.0 |
|---|---|---|
xml: of_remove(""), of_remove sin documento cargado, página que no responde | 0, como «nada quitado» | -5; -4; -2 — el recuento (>= 0) solo cuando se intentó quitar |
xml: una lectura (of_exists, of_count, of_get_value, of_is_valid…) o un cambio mientras un elemento construido sigue abierto | el documento se cargaba tal cual, y el resto de la construcción empezaba un NUEVO documento: lo anterior se perdía sin error | rechazada: -4, vacío o false, e is_last_error = «the document is still being built : close its elements first»; no se pierde nada (of_xml y of_pretty siguen disponibles) |
xml: of_add_xml bajo un elemento con espacio de nombres por defecto, o con el prefijo del destino (<s:Item/> bajo s:Body) | el fragmento quedaba fuera del espacio de nombres (xmlns=''), invisible para el XPath que había servido para añadirlo; el prefijo del destino se rechazaba (-4) | el fragmento hereda los espacios de nombres vigentes en su destino, como of_add_child |
xml: of_set_attr / of_add_child con un nombre con prefijo (xsi:type, p:item) | 0, un atributo fuera del espacio de nombres que el XPath no encontraba; con un prefijo no declarado, un documento que ya no se recargaba | en el espacio de nombres del prefijo (declarado en el nodo, o ligado con of_register_namespace); -4 y «undeclared prefix : xsi» si no está ligado en ninguna parte |
xml: of_pretty tras un of_add_child bajo un padre con prefijo (s:Body) | <Item> sin prefijo: releído, fuera del espacio de nombres | <s:Item>, como en of_xml |
xml: of_element_attr dos veces con el mismo nombre; un nombre :x, x: o a:b:c | dos atributos (documento mal formado); nombre aceptado (un documento que no se carga) | un solo atributo, con el segundo valor; nombre rechazado (is_last_error), el elemento se omite |
xml: of_transform | aplica una hoja XSLT al documento cargado | retirado: XSLT ya no existe en el motor (Chromium lo retira); transforme en el servidor o en PowerScript |
La receta: (1) un of_remove comprobado con = 0 (nada quitado) sigue funcionando; compruebe < 0 (nada pudo intentarse); (2) si leía un documento MIENTRAS lo construía (un of_exists para controlar, un of_count para numerar), cierre primero sus elementos, o lleve la cuenta usted mismo; (3) un fragmento de of_add_xml ya no tiene que redeclarar el espacio de nombres de su destino: el que escribía xmlns='' para salir de él todavía puede; (4) compruebe el retorno de of_set_attr / of_add_child con un nombre con prefijo, y declare el prefijo (o lígelo con of_register_namespace).
11.32 json: un array que se construye rápido, números que conservan sus cifras, un texto vacío que se dice vacío #
La segunda revisión de n_pbt_json no quita ni cambia ninguna firma. Añade las sobrecargas decimal y longlong de of_set_number y of_add_number, y los lectores of_get_decimal y of_get_longlong. Construir un array con of_add_* ya no recorre todo el array en cada añadido: 2.000 filas se construyen en una fracción de segundo en lugar de varios minutos. Dos reglas quedan ahora escritas en la documentación, sin cambiar: una cadena nula se escribe "" (para un null JSON, of_set_raw(path, "null")), y una clave escrita dos veces se lee en su PRIMERA aparición.
| Qué | 3.0 | 4.0 |
|---|---|---|
json: of_load de un texto hecho de blancos (salto de línea, tabulación) | -4, como un texto mal formado | -5, como un texto vacío |
json: of_load de un texto precedido de un BOM (archivo UTF-8 con BOM) | -4 | 0, el BOM se ignora |
json: of_load de un documento anidado en más de 512 niveles | 0 — y un texto muy profundo podía detener la aplicación | -4, la aplicación sigue viva |
json: of_set_number / of_add_number de un decimal (o de un literal como 690.50) | convertido a double: unas quince cifras, un céntimo podía perderse | sobrecarga decimal: todas las cifras, ceros finales quitados (690.5); un longlong también tiene la suya |
json: of_get_number de un exponente con ceros iniciales (1E-0000005) | 0, o un valor enorme | el valor correcto (0.00001) |
todos los componentes: \u seguido de cuatro caracteres que no son hexadecimales, en una cadena leída | los cuatro caracteres desaparecían | se conservan tal cual, como un escape desconocido |
La receta: (1) si comprobaba of_load(...) = -4 para reconocer un archivo vacío, compruebe < 0; (2) un importe decimal pasado a of_set_number se escribe ahora con todas sus cifras, sin cambiar nada en su código; (3) un identificador de más de 15 cifras se relee con of_get_longlong, un importe con of_get_decimal, ya no con of_get_number.
11.33 crosstab: of_from_datastore, rechazos que se dicen, el valor mostrado al doble clic #
La segunda revisión del crosstab renombra of_set_data a of_from_datastore, el nombre del datagrid y del scheduler, que leen un DataStore del mismo modo (mismos argumentos, mismos códigos). Toda adición, filtro o formato que nombra un campo desconocido devuelve ahora -5 en lugar de 0. La tabla solo dibuja las columnas visibles: un código de cliente o una fecha diaria puestos en Columnas ya no bloquean la pantalla. Novedades: is_thousands, CF_ICONS, las constantes VALUEFILTER_* y DATE_*.
| Qué | 3.0 | 4.0 |
|---|---|---|
| crosstab: alimentar desde un DataStore | of_set_data(ds) | of_from_datastore(ds), mismos códigos |
| crosstab: un campo desconocido (errata) pasado a una adición, un filtro, un formato, un orden, una agrupación de fechas | 0, no pasa nada | -5 |
crosstab: un tipo de filtro, una agregación, una parte de fecha fuera de la lista; una medida calculada en Filas; of_set_value_filter en una posición sin medida | 0; la posición se deslizaba a la última medida | -5 |
crosstab: of_set_layout de un texto que no es JSON; of_remove_calc_field / of_remove_calc_measure de un nombre desconocido | 0 | -5 |
crosstab: ad_value de ue_cell_double_clicked | el agregado bruto (400 bajo « 80,0 % »), 0 para una celda vacía | el valor mostrado (0.8), NULL para una celda vacía; un miembro vacío vale null en las tuplas |
| crosstab: el mismo campo en Filas (o Columnas, Filtros) Y en Valores | dependía del orden de las llamadas: el valor desaparecía; Quitar en una dimensión quitaba todo | permitido en todas partes; añadir o mover una dimensión conserva los valores, Quitar solo quita el chip señalado |
| crosstab: un miembro vacío (NULL, texto vacío); un texto con tabulación o salto de línea en el DataStore | « null » o una fila sin etiqueta; filas fantasma, columnas desplazadas | un solo miembro (vacío), al final; el texto se queda en su fila |
crosstab: is_label tras un nuevo of_from_datastore; is_label = "" | perdido; un chip sin texto | conservado; "" restablece el encabezado del DataWindow |
| crosstab: Ctrl+C en modo demo | ue_copy lanzado, portapapeles escrito | rechazado como una exportación, la cuadrícula lo indica |
crosstab: of_collapse_all; filtro de miembros de la cuadrícula; of_export_xlsx más allá de 16 384 columnas; exportaciones en VALUES_ROWS | solo filas; aplicado en cada casilla; un archivo ilegible; las medidas una al lado de otra | filas y columnas; aplicado con OK (10 000 miembros listados como máximo); rechazado, ue_xlsx_saved dice por qué; como en pantalla |
La receta: (1) sustituya of_set_data por of_from_datastore; (2) si ignoraba el código devuelto por of_add_*_field o por un filtro, compruebe < 0 — una errata por fin se ve; (3) en ue_cell_double_clicked, compruebe IsNull(ad_value) antes de calcular, y lea ad_value como el valor mostrado (una parte, no una suma, en modo « % »).
11.34 Base común: of_count y of_keys_at cuentan en long #
of_count devolvía un integer y of_keys_at tomaba una posición integer — en todos los componentes, en sus handles y en los objetos no visuales que los llevan (commandpalette, messagebox, speechout, toaster). Más allá de 32 767 elementos el recuento se desbordaba: un calendario (scheduler) alimentado por un DataStore de 40 000 filas ya no se contaba, y sus últimas filas no se recorrían. Las cuatro firmas son ahora long: long of_count ( ), long of_count ( string as_keys ), string of_keys_at ( long al_index ), string of_keys_at ( string as_keys, long al_index ).
La receta: una llamada no cambia. Una variable integer que recibe of_count() sigue siendo correcta hasta 32 767: declárela long. Un descendiente que sobrescribía of_count o of_keys_at adopta la nueva firma — el compilador le muestra la línea.
11.35 shellexplorer: un refresco que conserva el estado, cada fila para sí #
La revisión del explorador del shell conserva sus eventos: of_select, of_expand y of_collapse los lanzan como un clic, como en la 3.0. of_refresh e ib_show_files ya no cierran el árbol. Novedades: is_file_filter (los archivos mostrados), ib_show_hidden (los elementos ocultos, por defecto el ajuste del Explorador), ib_enabled y of_refresh(ruta), que relee una sola rama.
| Qué | 3.0 | 4.0 |
|---|---|---|
shellexplorer: of_refresh(), o ib_show_files cambiado | el árbol se cerraba, la selección desaparecía sin evento | las ramas abiertas y la selección vuelven, encontradas por su ruta; una selección que ya no existe dispara ue_selected("", "") |
shellexplorer: of_expand, of_select, of_collapse de una ruta vacía | 0 | -5 |
shellexplorer: of_expand de un archivo o de una carpeta sin hijos | nada | ue_path_not_found(…, "expand", "not a branch") |
shellexplorer: of_keys_at(ruta, i) | ruta/hijo, que nada sabía releer | la ruta del hijo, completa: se reinyecta en of_has, of_select, of_expand |
| shellexplorer: una misma carpeta en dos niveles (la Música del Escritorio y la de la carpeta del usuario) | un clic, un chevron o un doble clic en una actuaba sobre la otra | cada fila responde por sí misma |
| shellexplorer: archivos y carpetas ocultos | nunca mostrados | mostrados según el ajuste del Explorador del puesto, o ib_show_hidden |
| shellexplorer: una rama en un recurso que no responde | «…» para siempre, aun cerrada y reabierta | ue_error «… : timeout» a los 30 segundos; la rama se vuelve a pedir al reabrirse |
La receta: (1) ue_selected y ue_expanded se lanzan también tras un of_select o un of_expand de su código, una vez alcanzada la carpeta (una ruta aún por leer llega un poco más tarde): haga su trabajo en el evento, no justo detrás de la llamada, o relea of_selected_key() una vez llegada la selección; (2) en ue_selected, trate as_path = "" (la selección ha desaparecido); (3) compruebe -5 en una ruta vacía; (4) ponga ib_show_hidden = false si sus usuarios nunca deben ver los elementos ocultos, diga lo que diga su Explorador.
11.36 commandpalette: cada objeto solo toca su paleta, un cierre que siempre se anuncia, el idioma de la aplicación #
Solo hay una paleta en pantalla a la vez: cuando un segundo objeto paleta abre la suya, sustituye a la primera. Un objeto ya solo responde de su paleta y de su atajo. ue_opened y ue_closed se siguen disparando en of_open / of_close: dicen el ciclo de vida de la ventana, no un gesto (misma regla que los reproductores de audio y vídeo). Novedades: of_insert_command (un comando en una posición elegida), of_anchor_under (la paleta bajo un control, en píxeles de pantalla), y en el handle is_group, is_hint, is_keywords, is_icon.
| Qué | 3.0 | 4.0 |
|---|---|---|
commandpalette: ue_closed | sin argumento; nada en absoluto cuando la paleta moría antes de aparecer (cerrada o sustituida enseguida, ventana rechazada) | ue_closed(string as_reason) sigue a cada of_open que devolvió 0: "", cancelled, failed o blocked |
commandpalette: of_is_open, of_close, of_reset, destruir un objeto cuya paleta fue sustituida por la de otro | respondían VERDADERO por la paleta del otro, y la cerraban | solo responden de su paleta: FALSO, y no se cierra nada |
| commandpalette: destruir (o restablecer) un objeto cuyo atajo ha retomado otro objeto paleta de la misma ventana | quitaba el atajo del otro: la tecla ya no respondía en ningún sitio | el atajo del otro se queda |
commandpalette: of_register_shortcut de una tecla que el gancho no nombra (ctrl+ù, win+k, space sin modificador) | 0 («puesto»), y la tecla nunca hacía nada | -5; se conserva el atajo anterior |
commandpalette: of_register_shortcut con is_shortcut vacío y ningún atajo puesto | 0 | 2 |
commandpalette: of_add_command de una clave que contiene una coma | 0 — y is_recent la releía como dos comandos | -5 |
| commandpalette: un grupo declarado en varias veces (File, Edit, luego File) | dos encabezados «File» | uno solo, en el lugar de su primera aparición; se conserva el orden de los comandos en el grupo |
| commandpalette: la búsqueda y los acentos | preferences no encontraba «Préférences» | los acentos no cuentan |
| commandpalette: el idioma | la paleta hablaba inglés dijera lo que dijera PBT_SetLanguage | el idioma de la aplicación; un is_placeholder puesto por usted no se traduce |
| commandpalette: el atajo mostrado en una fila, teclado AZERTY; AltGr en la búsqueda | Ctrl+A se leía Ctrl+Q; escribir € (AltGr+E) lanzaba el comando mostrado como Ctrl+Alt+E | la tecla se lee como la nombra la DLL; un carácter escrito nunca es un atajo |
commandpalette: un atajo puesto sin ipo_owner (toda la aplicación) antes del atajo de una ventana, en la misma tecla | ganaba también en esa ventana | el atajo de la ventana va primero |
commandpalette: POSITION_ABSOLUTE | un comando de clave x o y llevaba la paleta al borde de la pantalla; con dos pantallas iba a la de la ventana | il_x / il_y se respetan, en la pantalla del punto pedido |
commandpalette: inv_pump | variable interna que quedó pública | protegida: no forma parte de la API, nada que leer ni escribir |
commandpalette: un comando cuyo texto cita __license | la paleta no se abría (-5) | se abre: solo una palabra reservada escrita como clave sigue rechazada |
La receta: (1) si su código dispara él mismo ue_closed(), pásele un motivo (""); un script que solo lo recibe compila tal cual; (2) si esperaba ue_opened tras of_open, trate también ue_closed("cancelled"), "failed", "blocked"; (3) compruebe 2 y -5 en el retorno de of_register_shortcut; (4) renombre una clave de comando que contenga una coma.
11.37 radialmenu: una clave sola que apunta bien, una rueda abierta que sigue a la aplicación, una etiqueta que se lee #
La rueda se dibuja una vez, y la aplicación sigue funcionando mientras está en pantalla: lo que cambie entretanto ahora cuenta. El cubo ya no deletrea nada (no lo hacía desde la 3.0, dijera lo que dijera la documentación): es la rama señalada la que muestra su etiqueta entera.
| Qué | 3.0 | 4.0 |
|---|---|---|
radialmenu: of_item con una clave sola (of_item("csv")) | cambiaba la etiqueta y el estado de la única rama que la llevaba, pero sus colores no se dibujaban, y el manejador sobrevivía a la eliminación del padre | se resuelve en la dirección completa (import/csv): mismo manejador, colores incluidos, liberado con el padre. Ambigua o desconocida: el manejador no cambia nada |
| radialmenu: un manejador sobre una dirección aún no añadida | un color puesto a través de él coloreaba la rama añadida después en esa dirección, incluso tras of_clear | no escribe nada; of_clear olvida todos los colores |
radialmenu: of_show | no devolvía nada | devuelve un long: 0, o -2 si el componente no está creado |
radialmenu: of_show(-1, -1) | abría la rueda en el cursor | la abre en el punto (-1, -1), como cualquier coordenada negativa; en el cursor es of_show() |
| radialmenu: una rama retirada, vaciada, atenuada u oculta mientras la rueda está abierta | aún se elegía: ue_item_selected para una rama desaparecida | ya no se elige: ue_dismissed; of_clear, of_remove_item y of_reset cierran la rueda abierta |
| radialmenu: clic derecho en otro sitio mientras la rueda está abierta | la nueva rueda sustituía a la anterior sin ue_dismissed | ue_dismissed para la anterior, y luego la nueva rueda |
| radialmenu: etiqueta larga o vacía | cortada a dos líneas y legible en ninguna parte; vacía, una porción sin nada | la rama señalada la muestra entera; vacía, se muestra la clave |
| radialmenu: lectura de derecha a izquierda | la corona giraba en el sentido de las agujas del reloj, ← subía | la corona gira en el otro sentido, ← y → se intercambian |
La receta: (1) of_show devuelve un long: una llamada suelta compila tal cual; (2) sustituya of_show(-1, -1) por of_show(); (3) si deshacía en ue_dismissed lo que ponía al abrir, nada que hacer: ahora llega también cuando una rueda sustituye a otra; (4) un manejador obtenido con una clave que llevan dos ramas ya no cambia nada: dé la dirección.
11.38 toaster: eventos que dicen qué toast, rechazos que se dicen, un toast que espera a ser visto #
Los tres eventos del toast cambian de firma, of_add_button devuelve un código, y un toast ya no se pierde cuando la aplicación está minimizada.
| Qué | 3.0 | 4.0 |
|---|---|---|
toaster: ue_toast_clicked, ue_toast_action, ue_toast_dismissed | (string as_key …): as_key llevaba el identificador del toast, como texto | (long al_id, string as_key …): al_id es el número devuelto por of_show, as_key el is_key del toast (vacío sin clave) |
toaster: of_add_button | devolvía el número de botones; una clave ya ocupada, una clave vacía y un cuarto botón pasaban sin decir nada | devuelve 0; -5 sin añadir nada para una clave vacía, ya ocupada, que contiene / o una barra vertical, o un cuarto botón. El número se lee con of_count() |
toaster: of_show con un ajuste fuera de límites | mostraba de todos modos: un tipo desconocido como información, una esquina desconocida arriba a la izquierda | devuelve -5 y no muestra nada: is_kind, is_position o is_sound fuera de sus constantes, il_max_visible fuera de 0 a 20, il_timeout por debajo de TIMEOUT_AUTO |
toaster: la misma is_key mostrada por dos toasters | el segundo reemplazaba el toast del primero y le quitaba sus eventos; una actualización conservaba la esquina original | cada toaster tiene el suyo; una actualización toma también la esquina, el anclaje a la pantalla y la ventana de anclaje |
| toaster: el sonido | un sonido del sistema en cada toast, información y éxito incluidos; ninguno para un toast salido de la cola de espera | is_sound: SOUND_AUTO (predeterminado) = solo error y advertencia, SOUND_ALWAYS, SOUND_NEVER; se reproduce cuando se solicita el toast, aunque espere su sitio |
toaster: toasts anclados a la pantalla desde dos ventanas, o sin ipo_owner | se colocaban exactamente uno sobre otro; el límite se contaba por ventana | una sola pila por monitor y por esquina, e il_max_visible cuenta por monitor |
| toaster: un toast anclado a una ventana minimizada | se iba a -32 000 px y se apagaba sin haber sido visto | espera: no se muestra y su cuenta atrás no corre hasta que la ventana vuelva |
| toaster: un toast que llega mientras se pasa el puntero por la pila | descontaba su tiempo y podía irse durante la lectura | nace en pausa, como los demás |
toaster: PBT_SetLanguage, PBT_SetDefaultTheme, PBT_SetDefaultThemeAccent, PBT_SetDefaultFont, PBT_SetFlowDirection mientras se muestra un toast | no le llegaban; el tamaño de letra seguía en 11 pt | lo siguen enseguida, tamaño de letra incluido |
| toaster: el texto de un toast | no se podía seleccionar; agarrar la barra de desplazamiento de un texto largo podía responder y cerrar | se puede seleccionar y copiar (clic derecho, Copiar); la selección y la barra de desplazamiento ya no cierran el toast |
toaster: il_button_count | variable pública | privada: lea el número de botones con of_count() |
toaster: un toast cuyo texto cita __license | no se mostraba | se muestra: solo una palabra reservada escrita como clave sigue rechazada |
toaster: of_close de un toast en pantalla | ningún evento | ue_toast_dismissed, como su cruz; un toast aún en espera, nunca visto, no lanza nada |
La receta: (1) añada long al_id como primer argumento de sus ue_toast_clicked, ue_toast_action y ue_toast_dismissed, y sustituya Long(as_key) por al_id; as_key es ahora la clave del toast; (2) si leía el retorno de of_add_button como un número de botones, lea of_count(); (3) para mantener un sonido en una información o un éxito, establezca is_sound = SOUND_ALWAYS.
11.39 messagebox: un campo obligatorio vacío nunca se valida, opciones con claves únicas #
Cambian cinco comportamientos; ninguna firma.
| Qué | 3.0 | 4.0 | |
|---|---|---|---|
messagebox: ib_input_required y un botón predeterminado con cuenta atrás (of_add_button_timed) | el clic automático validaba el cuadro con el campo vacío (of_show devolvía el botón, of_input_value una cadena vacía) | la cuenta atrás se agota sin pulsar; el botón espera a que el campo se rellene | |
| messagebox: Esc o Alt+F4 en un botón a la vez predeterminado y de cancelación, campo obligatorio vacío | devolvía 1, como un clic en ese botón | devuelve 0: cerrado sin elección | |
messagebox: of_add_choice | aceptaba una clave ya usada, o que contiene / o ` | ` | devuelve -5 y no añade nada |
messagebox: of_choose sin ninguna opción | mostraba un cuadro con un único botón Cancelar | no muestra nada: cadena vacía, y of_get_last_error dice «no choice to show» | |
messagebox: los botones de los cuadros prediseñados (of_info, of_confirm, of_yes_no, of_prompt…) cuando la aplicación no ha fijado ningún idioma | «Ok» | «OK», la palabra del catálogo inglés («Cancel», «Yes», «No» para los demás) |
La receta: (1) un campo obligatorio se valida ahora a mano — si contaba con el clic automático para avanzar, dé al campo un valor por defecto (is_input_value); (2) dé a cada opción una clave única, sin / ni |, y compruebe -5 si sus claves vienen de datos.
11.40 Base común: el idioma anunciado por defecto, los números decimales de cualquier equipo #
Dos correcciones de la base común, válidas para todos los componentes; ninguna firma cambia.
| Qué | 3.0 | 4.0 |
|---|---|---|
el idioma que anuncia un componente (<html lang>) | el francés, mientras la aplicación no eligiera idioma | el inglés, el idioma predeterminado de los componentes; el idioma elegido por la aplicación siempre lo sustituye |
un número decimal dado a una propiedad (id_*) | solo se convertía la coma decimal: en un equipo cuyo separador decimal es otro carácter, el valor no llegaba | se convierte el separador decimal del equipo, sea cual sea |
La receta: nada que cambiar. Una aplicación que elige su idioma lo sigue viendo aplicado; un lector de pantalla que leía en francés un componente de una aplicación inglesa ahora lo lee en inglés.
11.41 Teclado, foco y espacio: lo que las pruebas de la 4.0 han enderezado #
Comportamientos vistos al usar los componentes en una aplicación real; ninguna firma cambia, se añade un método.
| Qué | 3.0 | 4.0 |
|---|---|---|
Intro sobre un CommandButton de PowerBuilder que tiene el foco | nada (la tecla se perdía) | el botón se pulsa (clicked), como en un cuadro de diálogo de Windows; el botón predeterminado de una barra no |
Un componente desactivado (ib_enabled = false) | Tab se detenía en él | sale del orden de tabulación; reactivado (o of_reset), recupera su sitio |
Un atajo Ctrl sobre un carácter sin tecla propia (AZERTY: - en la tecla 6, + con Mayús) | Ctrl+- nunca encontraba su atajo | tras la tecla, el carácter de la distribución activa: Ctrl+-, Ctrl++, Ctrl+,, Ctrl+. funcionan en cualquier teclado |
| Teclear en un menú abierto (búsqueda, letras subrayadas) | una tecla de la fila superior daba su cifra (AZERTY: - llegaba como 6) | el carácter de la distribución; una letra subrayada &1 sigue respondiendo a la tecla 1 |
El calendario del selector de fecha (toolbar) | solo con el ratón | también con el teclado: flechas (día, semana), Re Pág/Av Pág (mes), Inicio/Fin, Intro elige, Esc cierra |
| Alt con dos cintas (o barras de menús) en la misma ventana, sin subventana entre ellas | la primera creada | la más cercana al foco en la pantalla |
breadcrumb en una barra muy estrecha | el primer segmento nunca cedía: el lugar actual se reducía a nada, la ruta se desbordaba | tras el centro, el lugar actual cede hasta una letra y «…», luego el primer segmento; nada sale de la barra |
menubar: una elección hecha con el ratón en el desplegable | el foco se quedaba en la barra (Edición > Pegar no pegaba en ningún sitio) | el foco vuelve al control que lo tenía |
ribbon: escribir en un cuadro combinado y luego elegir en su lista | dos ue_combo_changed: el texto tecleado y luego la elección | uno solo, la elección |
toolbar: is_text = "" asignado en ue_text_changed, el foco en el campo | el texto antiguo volvía, con un segundo evento | el campo queda vacío |
| El foco devuelto a un componente tras una ventana de la biblioteca (paleta de comandos) | la página, sin elemento: el cursor del editor de código había desaparecido | el elemento que lo tenía |
Una etiqueta con letra subrayada (&File) de derecha a izquierda | letras desordenadas («ileF») | la palabra se lee en orden |
n_pbt_utils.of_dir_folders(as_dir, ref as_names[]) | DirList en un ListBox: 4,5 s para 30 000 carpetas, y la carpeta actual de la aplicación cambiaba | nuevo: las subcarpetas tal como las muestra el Explorador, ordenadas, en una pasada; -4 ilegible, -5 ruta vacía |
breadcrumb: of_add_children(as_keys, as_child_keys[], as_texts[]) | una rama cada vez (of_add_child): 30 000 subcarpetas abrían el menú en más de 20 s | nuevo: todo un nivel en una llamada |
La receta: nada que cambiar. Una aplicación cuyo CommandButton con el foco debía ignorar Intro lo desactiva (Enabled = false) o le quita el foco; para una carpeta voluminosa, sustituya el bucle de of_add_child por un único of_add_children.
11.42 Datos y teclado: lo que el segundo lote de pruebas de la 4.0 ha enderezado #
Comportamientos vistos al usar los componentes de datos en una aplicación real; ninguna firma cambia.
| Qué | 3.0 | 4.0 |
|---|---|---|
| Intro o Esc pulsadas en un componente que no las toma | perdidas: el CommandButton de PowerBuilder Default o Cancel no se disparaba | la tecla vuelve a la ventana de PowerBuilder, como promete el capítulo 6.4: Esc pulsa el botón Cancel, Intro el botón Default |
PBT_SetLanguage seguido enseguida de una orden al componente | el texto de un evento lanzado por esa orden (ue_error de xmltree) seguía en el idioma anterior | el idioma vale desde la orden siguiente |
jsontree, xmltree: el foco dado al componente (of_focus_webview, la tecla Tab) | se quedaba en la página: las flechas e Intro no iban a ninguna parte | el árbol lo toma; F3 y Mayús+F3 pasan a la coincidencia siguiente o anterior de la búsqueda |
xmltree: un error señalado DESPUÉS del último carácter de su línea | ninguna columna subrayada | el final de la línea está marcado (↵) |
codeeditor con ajuste de línea (ib_wrap) | la banda il_doc_line podía quedar una fila demasiado arriba, al final de la línea anterior | las bandas cubren todas las filas de su línea y siguen el redimensionado |
codeeditor: « Reemplazar todo » y of_replace_all en un texto largo | varios segundos cuando el foco estaba en el editor; más allá, of_replace_all podía devolver 0 | instantáneo, y se devuelve el número de reemplazos; Ctrl+H con una búsqueda vacía pone el cursor en « Buscar » |
crosstab: « Añadir a columnas » (o filas, filtros) desde el menú de un chip, con el teclado | el foco se perdía; solo Intro abría el menú de un chip | el foco sigue al chip a su nueva zona; Mayús+F10 y la tecla Menú también abren su menú |
json: miles de of_add_* seguidos | casi un segundo para 2 000 filas | unas decenas de milisegundos; el documento es el mismo |
La receta: nada que cambiar. Una aplicación que trataba ella misma Esc pulsada en un componente (un atajo registrado con of_register_shortcut) lo conserva: un atajo va antes que la ventana.
11.43 Contenido y medios: lo que el tercer lote de pruebas de la 4.0 ha enderezado #
Comportamientos vistos al usar los componentes de contenido y medios en una aplicación real; ninguna firma cambia.
| Qué | 3.0 | 4.0 |
|---|---|---|
speechout: of_speak y luego of_pause en el mismo script | ue_started y la primera ue_sentence llegaban antes de ue_paused: la voz se anunciaba como iniciada | ue_started se lanza cuando la voz empieza de verdad a hablar; una pausa puesta antes la retiene: nada se dice ni se anuncia hasta of_resume |
speechout: un idioma sin voz exacta en el equipo (fr-CA donde solo hay fr-FR) | ue_started anunciaba el idioma pedido (fr-CA) | ue_started anuncia el idioma realmente leído (fr-FR), después de ue_voice_fallback |
webbrowser: Tab tras el último control de la barra de direcciones | el foco salía del componente hacia la ventana de PowerBuilder | el foco entra en el sitio, como en un navegador; Mayús+Tab desde el sitio vuelve a la barra |
webbrowser: ib_private = true puesto cuando la navegación ya es privada | sin efecto: el sitio volvía a encontrar lo que había guardado en la sesión privada | cada vez una sesión privada nueva y vacía |
video en pantalla completa: un cuadro modal de la aplicación (un MessageBox en ue_ended) | oculto bajo la película, que le volvía a quitar el teclado: la aplicación parecía congelada | la película cede: el cuadro pasa delante y recibe el teclado; al cerrarlo, la película recupera la pantalla y Esc sale de ella |
tilesbox: una flecha en el borde, junto a un mosaico más pequeño | Flecha arriba en un mosaico grande de la primera fila saltaba de lado, al pequeño vecino | solo cuenta un mosaico completamente más allá del borde: en el borde, el foco se queda donde está |
dockcontainer: il_back_color / il_text_color de un panel apilado | perdidos en cuanto su pestaña estaba activa (tomaba el acento del tema) | la pestaña activa conserva los colores del panel, como su lengüeta en el riel |
progressbar: id_value puesto en cada vuelta de un bucle largo | unos 2,5 s para 20 000 filas | bastante menos de un segundo; la barra sigue avanzando durante el bucle |
La receta: nada que cambiar. Una aplicación que contaba con ue_started para saber que of_speak había sido aceptado lee en su lugar su código de retorno (0); la que compara el idioma de ue_started con is_lang conoce ahora el reemplazo también por esa diferencia, además de por ue_voice_fallback.
11.44 Datos: leer una celda, ordenar y buscar con más precisión (json, jsontree, xmltree, crosstab) #
Estas adiciones de la 4.0 no quitan ninguna firma. Dos cambian lo que usted ve sin que haya escrito nada: Ctrl+C del crosstab copia ahora los encabezados, y ue_cell_double_clicked recibe un argumento más.
| Qué | 3.0 | 4.0 |
|---|---|---|
crosstab: ue_cell_double_clicked | ( string as_row_tuple_json, string as_col_tuple_json, double ad_value ) | ( string as_row_tuple_json, string as_col_tuple_json, integer ai_measure, double ad_value ): ai_measure es la posición del valor pulsado dos veces en Valores, a partir de 1; con las dos tuplas, es la dirección que of_get_cell_value vuelve a leer |
crosstab: Ctrl+C sobre celdas | solo las cifras | los encabezados con ellas: los nombres de las columnas en una primera línea, el miembro de cada fila (su ruta, «North / Boston») en una primera columna; ib_copy_headers = false da solo las cifras |
crosstab: of_remove_field, of_get_cell_value | — | nuevos: quitar un campo de todas las zonas sin reconstruir la disposición; leer el valor de una celda por sus tuplas y la posición de su medida |
json: of_get_array, of_pretty (long al_indent) | — | nuevos: todos los elementos de un array en una llamada; una sangría en espacios, o el documento compacto en una línea con 0 |
jsontree, xmltree: la búsqueda | siempre sin distinguir mayúsculas, y dentro de las palabras | ib_find_match_case e ib_find_whole_word, como el codeeditor, con los conmutadores Aa y ab del cuadro de búsqueda; por defecto, la búsqueda sigue siendo la de la 3.0 |
La receta: un script de ue_cell_double_clicked no tiene nada que cambiar, el argumento se añade antes del valor. Una aplicación que pega el resultado de Ctrl+C en un proceso que solo espera cifras pone ib_copy_headers = false.
11.45 Navegación y contenedores: pestañas con el teclado, paneles con un doble clic (tab, dockcontainer, ribbon, stepbar, listbar) #
Estas adiciones de la 4.0 no quitan ninguna firma. Tres gestos cambian lo que ve un usuario sin que usted haya escrito nada: el clic central y Ctrl+Tab en un tab, y el doble clic en un panel de dockcontainer o en la barra de título de su ventana flotante.
| Qué | 3.0 | 4.0 |
|---|---|---|
tab: clic central en una pestaña | nada | cierra una pestaña que se puede cerrar, por el mismo camino que su cruz: ue_tab_closing si ib_veto_close = true, luego ue_tab_closed; una pestaña que no se puede cerrar, o desactivada, no hace nada |
tab: Ctrl+Tab, Ctrl+Mayús+Tab, Ctrl+Av Pág / Re Pág | nada | la pestaña siguiente o anterior que puede activarse, en bucle, esté el teclado en la banda o en una página alojada (un control PowerBuilder, otro componente); es un gesto: ue_selection_changing si ib_veto_selection = true, luego ue_selection_changed. Desde un control fuera del tab, la tecla se queda en la ventana |
dockcontainer: doble clic en un encabezado o una pestaña; en la barra de título de una ventana flotante | nada; la ventana flotante se maximizaba | desacopla el panel en una ventana flotante (ue_panel_floated); en la barra de título de esa ventana, lo vuelve a acoplar en su sitio (ue_panel_docked), como su cruz |
dockcontainer: clic derecho en un encabezado o una pestaña | nada | un menú Desacoplar, Cerrar, Cerrar los demás (los demás paneles que se pueden cerrar del mismo grupo); cada cierre plantea su propia pregunta |
dockcontainer: ib_veto_close, ue_panel_closing, of_close_panel, n_pbt_dock_panel.ib_closable | — | nuevos: la pregunta antes de cerrar un panel (opcional, false por defecto), el cierre desde el código (-4 si lo rechaza, -5 para el panel principal o un panel que no se puede cerrar), y la posibilidad de cerrar cambiada en vivo |
ribbon: n_pbt_ribbon_item.is_image, id_min, id_max, id_step | fijados al añadir | la imagen de un control y el rango de un contador cambian en vivo y se releen; el valor de un contador se devuelve a su nuevo rango sin lanzar ue_value_changed, como id_value |
stepbar: STATE_WARNING, STATE_SKIPPED, NAV_VISITED | — | nuevos: un paso superado con una advertencia (viñeta ámbar), un paso omitido (anillo discontinuo), y un modo en que todo paso ya alcanzado sigue siendo pulsable, también hacia delante tras un retroceso |
listbar: n_pbt_listbar_section.ib_pinned | — | nuevo: una sección anclada al pie de la barra (Configuración, Ayuda), que sigue visible cuando la barra se desplaza, también en el carril; la disposición guardada conserva el orden de las secciones |
La receta: nada que cambiar. En una página de tab, Ctrl+Tab cambia ahora de pestaña incluso cuando el foco está en un control de la página, como en una hoja de propiedades de Windows. Para maximizar una ventana flotante del dockcontainer, su botón Maximizar sigue ahí.
11.46 Menús: el chevron de la barra de menús, una lista desplegable en la barra de herramientas, diez mil comandos (menubar, toolbar, commandpalette) #
Estas adiciones de la 4.0 no quitan ninguna firma. Cambia un comportamiento por defecto: una barra de menús demasiado estrecha ya no salta de línea, pliega sus últimos títulos en un chevron. Y of_open de una paleta de comandos ya no se niega a abrirse sin ipo_owner ni ipo_receiver.
| Qué | 3.0 | 4.0 |
|---|---|---|
menubar: una barra demasiado estrecha para sus títulos | saltaba de línea: su altura seguía las filas, ue_auto_height en cada cambio de ancho | se queda en una línea: los títulos que no caben, desde el final, pasan al desplegable de un chevron (sus entradas en cascada, Alt + su letra los abre desde el chevron, las flechas se detienen en él como en un título, a la izquierda en RTL); la altura ya no cambia. ib_wrap = true devuelve el comportamiento anterior |
menubar: ib_track_hover, ue_item_hover, of_add_header, n_pbt_menubar_menu.is_align | — | nuevos: la entrada señalada de un desplegable, para el texto de ayuda de una barra de estado (opcional, false por defecto; una dirección vacía al cerrar), encabezados de sección en un desplegable, y un menú al final de la barra (ALIGN_END, Ayuda) |
toolbar: of_add_combo, of_add_combo_item, of_remove_combo_item, of_clear_combo, ue_combo_changed, n_pbt_toolbar_item.ib_editable | — | nuevos: una lista desplegable en la barra, editable o no, que sigue funcionando plegada en el chevron; su evento tiene el nombre y el sentido del de la cinta |
commandpalette: dos paletas sobre el mismo ipo_owner | compartían su cola de eventos (una podía recibir el ue_closed de la otra), y la segunda retomaba el atajo de la primera | cada una sus eventos y su atajo; el mismo atajo registrado por la segunda pasa a ella (1). of_open y of_register_shortcut ya no devuelven -5 sin ipo_owner ni ipo_receiver: los eventos siempre llegan |
commandpalette: miles de comandos | cada línea dibujada: la apertura y la escritura se ralentizaban | solo se dibujan las líneas visibles: 10.000 comandos se abren y se filtran sin espera |
| Atajos de toda la biblioteca: AltGr + una tecla que escribe un carácter (el euro de un teclado francés) | lanzaba el atajo Ctrl+Alt de esa tecla, también en un campo PowerBuilder | se escribe el carácter, no se lanza ningún atajo; AltGr solo ya no da el teclado a una barra de menús o a una cinta |
La receta: una barra de menús cuya altura variable era deseada pone ib_wrap = true; una aplicación que recolocaba sus controles en ue_auto_height en cada redimensionamiento ya no tiene nada que hacer después del primero. Un atajo de la aplicación en Ctrl+Alt + una letra que el teclado del usuario escribe con AltGr ya no se lanza mientras escribe: elija otra tecla.
11.47 Contenido y medios: un registro línea a línea, el tamaño real de una imagen, una película que espera (statictext, picture, video) #
Estas adiciones de la 4.0 no quitan ninguna firma ni cambian ningún valor por defecto. Dos comportamientos se precisan: el zoom de una imagen puede pasar su tope para alcanzar el tamaño real, y una película de una URL reproducida sin red lo dice.
| Qué | 3.0 | 4.0 |
|---|---|---|
statictext: of_append_text, ib_follow_end | — | nuevos: un registro escrito línea a línea — solo la línea añadida viaja al componente — y, como opción, la vista que se queda abajo mientras el lector no suba |
statictext: ib_auto_width, ue_auto_width | — | nuevos: el userobject toma la anchura de su texto (opcional, false por defecto), como hace ib_auto_height con la altura |
picture: id_max_zoom, la tecla 1 | el zoom se detenía en 8.0 veces el encuadre, pasara lo que pasara: un escaneo grande mostrado pequeño nunca se leía píxel a píxel | el tope se ajusta (id_max_zoom, 8.0 por defecto); la tecla 1 muestra el tamaño real, y el tope sube hasta él cuando pide más |
video: ue_buffering, of_is_buffering | una película que esperaba sus datos se quedaba congelada sin decir nada | un anillo con el acento del tema gira sobre la imagen, y ue_buffering(true) y luego ue_buffering(false) enmarcan la espera |
video: una película de una URL mientras el puesto no tiene red | «este archivo no se encuentra o no se puede reproducir», ue_failed con REASON_UNSUPPORTED | «sin conexión de red» sobre la imagen, ue_failed con REASON_NETWORK |
La receta: nada que cambiar. Un registro que reescribía is_text en cada línea pasa a of_append_text; un código que comprobaba REASON_UNSUPPORTED para una película de una URL sin red comprueba REASON_NETWORK.
11.48 Revisión del 03/10/2026: una disposición restaurada se avisa, una exportación a una ruta relativa cae en el lugar correcto (toolbar, crosstab) #
Ninguna firma cambia. Dos comportamientos se alinean con la regla de la biblioteca: todo cambio de la disposición se avisa, y una ruta relativa de escritura se resuelve como en las demás exportaciones.
| Qué | 3.0 | 4.0 |
|---|---|---|
toolbar: of_set_layout | ningún evento | emite ue_layout_changed, como el dockcontainer y el datagrid: todo cambio de la disposición se avisa, venga del usuario o de su código |
crosstab: of_export_csv, of_export_xlsx a una ruta relativa | escrito en la carpeta actual del momento — que un DirList o un cuadro de archivos podían haber movido; ue_csv_saved / ue_xlsx_saved devolvían la ruta tal como se dio | escrito en la carpeta donde arrancó la aplicación, como las demás exportaciones nativas; el evento devuelve la ruta completa; una ruta que depende de la carpeta actual de una unidad (\x, C:x) se rechaza |
La receta: una aplicación que guarda la disposición en ue_layout_changed vuelve a guardar el mismo valor tras of_set_layout — sin consecuencias. Una exportación que contaba con la carpeta actual del momento indica una ruta completa.
← Migrar de la 2.0 a la 3.0 · Índice · Notas de la versión →