# 3. Base comum `u_pbt_base` #

← Primeiros passos · Índice · Temas →

Todos os componentes visuais herdam de `u_pbt_base`, que fornece o ciclo de vida, o motor de propriedades, o transporte para o componente web e a gestão de erros. As propriedades propriamente ditas — tema e tooltips incluídos — são publicadas por cada componente: a página do componente apresenta a lista completa. `u_pbt_base` nunca é utilizado diretamente — coloca-se sempre um componente concreto — mas tudo o que se segue está disponível em qualquer lado.

## 3.1 O motor de propriedades #

### Atribuir #

Qualquer valor controlável é uma variável de instância pública, atribuída diretamente:

    uo_progress.id_value   = 42.5
    uo_progress.is_label   = "Importação em curso…"
    uo_progress.ib_animated = true

O prefixo húngaro indica o tipo: `is_` string, `ib_` boolean, `ii_` integer, `il_` long (muitas vezes uma cor `RGB()`), `id_` double.

Não existe nenhum `of_set_xxx` escalar: uma propriedade define-se por atribuição. Continuam a ser métodos as adições, remoções e ações (`of_add_*`, `of_remove_*`, `of_select_*`, `of_reset`, `of_set_layout`…).

### Reler #

A leitura devolve o último valor definido (cache do lado do PowerBuilder):

    if uo_progress.id_value >= 100 then …

Um componente web não pode ser interrogado de forma síncrona: essa cache é, portanto, atualizada pelos eventos. Sempre que o componente move uma propriedade por si próprio — o utilizador segue uma ligação, amplia com a roda, recolhe o friso, escreve texto — o evento que o avisa atualiza a propriedade de passagem. A releitura devolve então o estado real, e o novo valor já está colocado quando o código do evento é executado.

O mesmo vale para os itens: após um clique do utilizador, `of_item(...)` relê o que está no ecrã — a entrada selecionada, a secção recolhida, o botão marcado.

Uma propriedade que nenhum evento acompanha permanece no último valor que definiu.

### Agrupar as modificações #

Uma rajada de atribuições provoca outros tantos desenhos. `of_set_redraw` funde-os num único:

    uo_grid.of_set_redraw(false)
    … vinte atribuicoes e of_add_* …
    uo_grid.of_set_redraw(true)     // UM unico repaint

Devem chamar-se sempre os dois (o `true` final não é opcional).

## 3.2 Os items #

Um componente com conteúdo (separadores, botões, painéis, mosaicos, secções…) expõe os seus elementos através de handles tipados, obtidos a partir do componente ou do respetivo elemento pai.

### Adicionar #

A adição devolve o handle do elemento criado:

    n_pbt_tab_page lnv_page

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

### Localizar e modificar #

`of_item(id)` — ou a fábrica do nível em causa — devolve o handle de um elemento existente; as respetivas propriedades definem-se como as de um componente:

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

### Hierarquias: um identificador só é único dentro do respetivo elemento pai #

Um componente com vários níveis não expõe qualquer atalho para a folha: o caminho é obrigatório, o que garante que nenhum identificador seja ambíguo.

    // Friso : separador > grupo > controlo > entrada de menu
    uo_ribbon.of_tab("home").of_group("clipboard").of_item("paste").ib_enabled = false

Os eventos transportam igualmente o caminho completo:

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

### Eventos de item #

Não existem eventos de item genéricos no antecessor: um identificador de folha isolado seria ambíguo assim que os items são aninhados (uma toolbar tem várias barras, uma tilesbox vários grupos…). Cada componente declara portanto os seus eventos de item, com o caminho completo: `ue_item_selected (as_section, as_id)` para a listbar, `ue_tile_clicked (as_group, as_id)` para a tilesbox, `ue_clicked (as_bar, as_id)` para a toolbar…

Consulte a página do componente: é aí que consta a lista exata.

## 3.3 Eventos comuns a todos os componentes #

Evento | Acionado quando
--- | ---
`ue_ready ( )` | O componente terminou o carregamento; tudo o que foi enviado antes foi reproduzido
`ue_runtime_missing ( )` | O runtime WebView2 está ausente — ver Instalação
`ue_bg_color (long al_color)` | O componente calculou a cor de fundo do respetivo tema; o userobject já adotou essa cor (`backcolor`), cabendo ao programador harmonizar a window se necessário

> Os comandos enviados antes de `ue_ready` não se perdem: são colocados em fila e reproduzidos pela mesma ordem. É, portanto, possível configurar tudo logo no `constructor` ou no `open`.

    // evento ue_bg_color : harmonizar a window com o fundo do componente
    parent.backcolor = al_color

## 3.4 Propriedades e eventos opcionais (opt-in) #

Determinadas funcionalidades não estão ativas por predefinição: só são publicadas pelos componentes onde fazem sentido e têm de ser pedidas.

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

O componente mede a sua altura ideal e redimensiona o userobject; o evento `ue_auto_height(al_height)` permite reposicionar os controlos vizinhos.

    uo_entete.ib_auto_height = true

    // evento ue_auto_height de uo_entete
    il_hauteur_entete = al_height
    of_relayout()          // reposiciona o conteudo por baixo

Publicada por: `picture` e `statictext`.

As faixas não publicam esta propriedade — a sua altura é intrínseca. O `ribbon` e a `toolbar` não deslocam verticalmente: uma altura fixa só pode produzir espaço vazio por baixo da faixa ou conteúdo truncado (faixa de opções recolhida, barra de ferramentas distribuída por duas filas…). Ajustam-se, portanto, sempre, sem nada para ativar, e continuam a publicar `ue_auto_height` para que possa reposicionar o que fica por baixo.

### Largura automática — `ib_auto_width` #

O mesmo princípio aplicado à largura. Publicada unicamente pelo `listbar`, o único componente cuja largura natural tem significado.

Uma listbar recolhida numa calha encolhe sozinha e devolve a largura ao expandir-se: `ib_auto_width` só lhe serve se quiser acompanhar também a largura expandida (a barra ajusta-se então à etiqueta mais comprida).

### Eventos de rato ambientais — `ib_track_mouse` #

Os eventos de rato de alta frequência estão cortados na origem: sem subscrição, o componente não os emite (nada atravessa a ponte para o PowerBuilder).

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

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

Os eventos discretos (clique, seleção, menu, largada…) são sempre emitidos, sem subscrição.

## 3.5 Os atalhos de teclado #

Um atalho aciona um componente esteja o foco onde estiver na window — o utilizador não tem de voltar ao botão para o acionar. Qualquer componente visual os aceita, sem nada a ativar.

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

### Escrever um acorde de teclas #

O acorde é uma cadeia livre, normalizada pela biblioteca: as maiúsculas, os espaços e a ordem dos modificadores não têm qualquer importância. `"Ctrl+Shift+S"`, `"ctrl + shift + s"` e `"SHIFT+CTRL+S"` designam o mesmo atalho — é impossível registar duas variantes por descuido.

Elemento | Formas aceites
--- | ---
Modificadores | `Ctrl` (ou `Control`), `Alt`, `Shift` — combináveis, por qualquer ordem
Tecla | uma letra `A`–`Z`, um algarismo `0`–`9`, `F1` … `F24`, `Enter` (ou `Return`), `Escape` (ou `Esc`), `Delete` (ou `Del`), `Insert`, `Home`, `End`, `PageUp`, `PageDown`

Esta lista é exaustiva: uma tecla ausente (Tab, Espaço, uma tecla do teclado numérico, um sinal de pontuação) não aciona qualquer atalho.

Uma tecla isolada é um acorde válido (`"F5"`). Uma cadeia vazia retira o atalho do componente.

> `"Enter"` e `"Escape"` isoladas não se registam como atalhos: estas duas teclas continuam reservadas ao botão predefinido e ao botão de cancelamento (`ib_default` / `ib_cancel` do button). Combinadas com um modificador, voltam a ser acordes vulgares (`"Ctrl+Enter"`).

### Quem ganha em caso de conflito #

Dois componentes podem pedir o mesmo acorde — algo frequente quando uma window aloja várias zonas com o seu próprio «Guardar». O arbítrio faz-se por esta ordem:

1. o componente que tem o foco de teclado prevalece sobre todos os outros: o atalho de uma zona ativa nunca é eclipsado por um vizinho;
2. na falta dele, ganha o primeiro registado.

Apenas os componentes visíveis e ativos da window em primeiro plano concorrem. Voltar a registar um acorde num componente que já tinha um substitui-o sem alterar a sua posição: reconfigurar uma window não baralha as prioridades.

### Atalhos de item #

A sobrecarga de dois argumentos liga o acorde a um item do componente em vez do componente inteiro — o segundo argumento é o identificador do item:

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

### Retirar os atalhos #

    uo_barre.of_clear_shortcuts()      // componente E items

`of_reset()` e a destruição do componente chamam `of_clear_shortcuts()` por si: um componente que desapareceu nunca mantém um acorde reservado.

### A tecla Alt #

A tecla Alt isolada não é capturada: dá o foco ao ribbon, que mostra as suas keytips (ver ribbon). A biblioteca não interceta, portanto, as teclas seguintes — é o ribbon que as lê, como se o utilizador lhe tivesse clicado. Esc ou um segundo Alt devolvem o foco ao controlo abandonado. Se nenhum ribbon da window declarar keytips, Alt mantém o seu comportamento habitual do Windows.

Membro | Efeito
--- | ---
`of_register_shortcut (string as_chord)` | Declara um atalho para o componente; uma cadeia vazia retira-o
`of_register_shortcut (string as_chord, string as_key)` | Declara um atalho para um item, designado pelo seu identificador
`of_clear_shortcuts ( )` | Retira todos os atalhos do componente, items incluídos

## 3.6 Repor um componente a zero: `of_reset()` #

`of_reset()` devolve o componente ao seu estado inicial, como se tivesse acabado de ser carregado:

- o conteúdo é esvaziado (items, páginas, painéis…);
- cada propriedade regressa à sua predefinição (formatação, cores, modo, etiquetas);
- as substituições de estilo e os tooltips definidos na instância são anulados;
- a cache de propriedades do lado do PowerBuilder é esvaziada (as releituras partem novamente das predefinições);
- o estado nativo é igualmente reposto (menu de contexto, modo de apresentação…).

    uo_grid.of_reset()          // recomecar com uma grelha vazia
    // ... depois reconstruir

> ⚠️ Reutilizar uma instância para apresentar outra coisa sem chamar `of_reset()` mantém o estado anterior (uma cor, um modo, uma altura automática). É a causa mais frequente de um «resto de apresentação» inexplicado.

## 3.7 Diagnóstico #

Membro | Efeito
--- | ---
`of_is_created ( )` → boolean | O componente nativo existe (runtime presente, anfitrião válido)
`of_is_ready ( )` → boolean | O conteúdo web está carregado (`ue_ready` já acionado)
`of_get_last_error ( )` → string | Última mensagem de erro detalhada da DLL, após um retorno `< 0`

Códigos de retorno dos métodos `of_*`:

Retorno | Significado
--- | ---
`≥ 0` | OK (aplicado ou colocado em fila)
`-2` | Componente não criado (runtime ausente, anfitrião inválido)
`-4` | Operação falhada (captura, escrita de ficheiro…)
`-5` | Argumento inválido (identificador vazio, valor fora dos limites)
`-6` | Runtime WebView2 demasiado antigo para a funcionalidade pedida (impressão)

## 3.8 Exportar a representação como imagem #

Qualquer componente sabe exportar-se como imagem, tal como é apresentado:

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

Para o imprimir em vez de o exportar, ver Imprimir.

Útil para um relatório, um anexo de e-mail ou um registo de incidente. O componente tem de estar criado e o seu conteúdo carregado.

## 3.9 Ciclo de vida #

1. Construção: o webview é criado logo na construção do userobject — indispensável para o alojamento (separadores, painéis acopláveis): um webview criado depois da mudança de elemento pai do respetivo HWND não é apresentado.
2. Fila de espera: os comandos são colocados em fila enquanto `ue_ready` não for acionado.
3. Pronto: `ue_ready`; a fila é reproduzida pela mesma ordem.
4. Redimensionamento: automático, o componente acompanha o tamanho do userobject.
5. Destruição: ao fechar a window; o webview é libertado, sem qualquer processo órfão.

Deve chamar-se `PBT_Warmup()` uma vez no arranque da aplicação para que este ciclo seja impercetível (Instalação).

## 3.10 Boas práticas #

- Definir o tema predefinido e o idioma no objeto de aplicação, antes da abertura da primeira window: os componentes deixam de apresentar qualquer flash de estilo.
- Enquadrar toda a construção volumosa com `of_set_redraw(false)` / `of_set_redraw(true)`.
- Chamar `of_reset()` antes de reutilizar uma instância para outro conteúdo.
- Não bloquear a thread de UI com um ciclo PowerScript longo entre a criação e a apresentação: a inicialização do webview precisa do ciclo de mensagens (ver FAQ).

← Primeiros passos · Índice · Temas →