# tab — `u_pbt_tab` #

← Referência dos componentes · Índice do guia

> Separadores modernos que alojam controlos reais do PowerBuilder: faixa reposicionável, ícones por separador, separadores que se podem fechar, reordenação com o rato.

> ▶ Ver ao vivo — Aplicação de demonstração, mosaico Tab: a pré-visualização, o código que o produz e esta página, lado a lado.

## Em resumo #

Userobject | `u_pbt_tab`
Classe de items | `n_pbt_tab_page` (uma página = um separador)
Serve para | Substituir um `tab` do PowerBuilder por uma faixa com tema, mantendo os ecrãs existentes como páginas
Opções opt-in | `ib_reorderable`

O argumento decisivo: uma página não é uma maqueta HTML, é um `dragobject` do PowerBuilder — um userobject, uma DataWindow, um conjunto de controlos ou até outro componente PBToolboxAI. O componente encarrega-se do posicionamento, do redimensionamento e da apresentação/ocultação na mudança de separador. Ver Alojar controlos reais do PowerBuilder.

## Início rápido #

    // event open da janela : cada pagina e um controlo PB ja colocado na janela
    uo_onglets.of_add_page("clients",  "Clientes",  uo_page_clients)
    uo_onglets.of_add_page("factures", "Faturas", uo_page_factures, /*fechavel*/ true)

    // Um icone por separador, colocado no handle da pagina
    uo_onglets.of_item("clients").is_icon  = "mono:img\clients.svg"
    uo_onglets.of_item("factures").is_icon = "mono:img\invoice.svg"

    uo_onglets.of_select_page("clients")

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

## Propriedades #

Propriedade | Tipo | Predefinição | Função
--- | --- | --- | ---
`is_position` | string | `"top"` | Colocação da faixa: `top`, `bottom` (banda horizontal), `start`, `end` (banda vertical). Constantes `POSITION_TOP`, `POSITION_BOTTOM`, `POSITION_START`, `POSITION_END`. `START`/`END` são lógicas e acompanham o sentido de escrita
`is_overflow_mode` | string | `"menu"` | O que faz uma banda demasiado pequena para os seus separadores. `menu` (predefinição) mantém cada separador legível: os que já não cabem saem da banda e o botão `···` lista-os todos, ficando o separador ativo sempre visível. `compact` mantém-nos todos na banda e deixa-os reduzir-se, até ao espaço que um texto truncado exige. Constantes `OVERFLOW_MENU`, `OVERFLOW_COMPACT`
`ib_reorderable` | boolean | `false` | Opt-in: o utilizador pode reordenar os separadores arrastando o respetivo cabeçalho com o rato (aciona `ue_tab_reordered`)
`ib_veto_selection` | boolean | `true` | Perguntar antes de mudar de separador ativo (aciona `ue_selection_changing`, que pode recusar). Ativo por predefinição: não programar nada deixa sempre passar. Coloque-o a `false` para eliminar a ida e volta ao PowerBuilder (~35 ms) onde se notaria
`ib_veto_close` | boolean | `true` | Perguntar antes de fechar um separador que se pode fechar (aciona `ue_tab_closing`, que pode recusar). Ativo por predefinição, a mesma regra
`ib_context_menu` | boolean | `true` | Menu contextual integrado no cabeçalho de um separador: Fechar, Fechar os outros, Fechar à direita, Fechar todos, esbatidos quando não têm alvo. Ativo por predefinição. O clique direito seleciona primeiro o separador: se a aplicação recusar a mudança, não abre nenhum menu. Cada fecho passa por `ue_tab_closing`, uma pergunta por página: a que detém trabalho não guardado recusa sozinha e permanece aberta enquanto as vizinhas partem. A `false` para mostrar o seu próprio menu a partir de `ue_rclicked`
`is_theme_style` | string | `fluent` | Estilo visual do componente (constantes `THEME_STYLE_*`)
`is_theme_mode` | string | `light` | Variante clara ou escura (constantes `THEME_MODE_*`)
`il_theme_accent` | long | `-1` | Cor de destaque deste componente (`-1` = destaque do tema)
`is_tooltip` | string | `""` | Tooltip simples apresentado ao passar sobre o componente
`is_super_tooltip_title` | string | `""` | Título do tooltip enriquecido (prevalece sobre `is_tooltip`)
`is_super_tooltip_text` | string | `""` | Texto do tooltip enriquecido (aceita marcação enriquecida)
`is_super_tooltip_image` | string | `""` | Imagem do tooltip enriquecido

### Propriedades de uma página — `n_pbt_tab_page` #

Obtidas através de `of_item(id)`, podem ser alteradas a quente, sem reconstruir a faixa.

Propriedade | Tipo | Predefinição | Função
--- | --- | --- | ---
`is_title` | string | `""` | Etiqueta do separador. Aceita a formatação com etiquetas
`is_icon` | string | `""` | Ícone do separador (formas aceites: caminho, `mono:`, `tint:`, recurso de DLL). Vazio = nenhum ícone
`ib_enabled` | boolean | `true` | `false` inativa o separador e impede a sua seleção pelo utilizador
`ib_visible` | boolean | `true` | `false` oculta o separador sem eliminar a página; ocultar o separador ativo ativa um vizinho
`ib_closable` | boolean | `false` | Cruz de fecho neste separador. Raramente conhecido no momento em que a pagina e adicionada: passa a `true` assim que o documento que contem e guardado, e a `false` enquanto la corre um processamento
`is_shortcut` | string | `""` | Atalho de teclado que ativa este separador: `"Ctrl+2"`, `"Alt+F"`, `"F6"`… Responde onde quer que o utilizador esteja na janela, incluindo a página alojada — o atalho é detido pela DLL, não pela barra de separadores. Selecionar assim é um clique: `ue_selection_changing` é perguntado, `ue_selection_changed` segue. `""` remove-o
`is_tooltip` | string | `""` | Tooltip simples apresentado ao passar sobre o item
`is_super_tooltip_title` | string | `""` | Título do tooltip enriquecido do item (prevalece sobre `is_tooltip`)
`is_super_tooltip_text` | string | `""` | Texto do tooltip enriquecido do item (aceita marcação enriquecida)
`is_super_tooltip_image` | string | `""` | Imagem do tooltip enriquecido do item

## Métodos #

Método | Função
--- | ---
`of_add_page (string as_id, string as_title, dragobject ado_page)` | Acrescenta um separador que não se pode fechar, sem ícone, e aloja o controlo
`of_add_page (string as_id, string as_title, dragobject ado_page, boolean ab_closable)` | Idem, com cruz de fecho se `ab_closable`
`of_add_page (string as_id, string as_title, dragobject ado_page, boolean ab_closable, string as_icon)` | Idem, com o ícone colocado de imediato (o separador surge logo completo)
`of_insert_page (string as_id, string as_title, dragobject ado_page, integer ai_index)` | Acrescenta a página e desloca-a depois para a posição `ai_index`
`of_insert_page (string as_id, string as_title, dragobject ado_page, boolean ab_closable, string as_icon, integer ai_index)` | Idem, dizendo o que `of_add_page` diz : se o separador fecha, e o seu ícone
`of_move_page (string as_id, integer ai_index)` | Desloca um separador existente; a página alojada e a seleção são preservadas
`of_select_page (string as_id)` | Ativa um separador — rigorosamente equivalente a um clique do utilizador (passa por `ue_selection_changing` e depois `ue_selection_changed`)
`of_remove_page (string as_id)` | Retira o separador e devolve o controlo à sua janela de origem, oculto. O controlo continua utilizável e pode ser alojado noutro local
`of_selected_key ( )` | Identificador do separador ativo, `""` se não existir nenhum
`of_get_layout ( )` | Relê a disposição atual em JSON: a ordem dos separadores e a visibilidade de cada um. Guarde-a (ficheiro, base de dados, registo) e devolva-a com `of_set_layout` no arranque seguinte. O mesmo par tem os mesmos nomes em todos os componentes reorganizáveis
`of_set_layout (string as_layout_json)` | Repõe uma disposição lida com `of_get_layout` ou recebida com `ue_layout_changed`. O que a disposição não nomeia mantém o seu lugar no fim: uma disposição de ontem não deve fazer desaparecer o que foi acrescentado desde então. Aplicá-la não gera qualquer event — foi você que a forneceu
`of_item (string as_id)` | Handle `n_pbt_tab_page` da página (criado no primeiro acesso)
`of_refresh_page (string as_id)` | Atualiza a representação de uma página construída fora do ecrã, sem cintilação
`of_relayout ( )` | Volta a publicar a zona de página para que o controlo alojado seja reposicionado (útil após uma apresentação diferida)
`of_preload_icons (string as_icons[])` | Pré-aquece um lote de ícones no arranque: um separador aberto mais tarde apresenta o seu ícone de imediato
`of_reset ( )` | Esvazia a faixa: cada página alojada é devolvida à sua janela de origem e o componente regressa ao estado inicial
`of_set_redraw (boolean)` | Agrupa uma rajada de alterações numa única representação
`of_save_as_png (string)` · `of_save_as_jpg (string)` | Exporta a representação como imagem

> `of_add_page` devolve um código negativo se o identificador estiver vazio, já estiver a ser utilizado ou se o controlo indicado não for válido.

Quando há demasiados separadores — reduzem-se para caber na banda, mas nunca abaixo de uma largura legível: o ícone e a cruz de fecho ocupam o seu espaço para além do título, nunca no lugar dele. Os que já não cabem saem da banda, e na sua extremidade aparece um botão `···`: abre a lista de todos os separadores visíveis, com o ativo assinalado, e a escolha passa para ele. O separador ativo permanece sempre visível. A banda não desliza — esse botão é o caminho para os separadores fora do enquadramento. Nada a programar, o comportamento é automático.

## Eventos #

Evento | Acionado quando
--- | ---
`ue_selection_changed (string as_from_id, string as_id)` | O separador ativo mudou — por clique ou por `of_select_page`. Mesmos argumentos que `ue_selection_changing`: a pergunta e o seu resultado leem-se da mesma forma, e `as_from_id` é a página abandonada
`ue_tab_closed (string as_id)` | O utilizador clica na cruz de um separador que se pode fechar. O separador já foi retirado e a página devolvida à sua janela
`ue_tab_reordered (string as_id, integer ai_index)` | O utilizador terminou de arrastar um cabeçalho. `ai_index` é a nova posição, a partir de 1
`ue_layout_changed (string as_layout_json)` | A disposição mudou — o utilizador reorganizou algo, ou o seu próprio código. Transporta a disposição inteira, não apenas o que se moveu: guardá-la é uma única atribuição
`ue_selection_changing (string as_from_id, string as_id)` → boolean | Cancelável, colocado antes de o separador ativo mudar. Lançado por predefinição; `ib_veto_selection = false` elimina-o. Devolva `false` para manter o utilizador na sua página
`ue_tab_closing (string as_id)` → boolean | Cancelável, colocado antes do fecho de um separador que se pode fechar. Lançado por predefinição; `ib_veto_close = false` elimina-o. Nomeado para fazer par com `ue_tab_closed`: closing / closed. Devolva `false` para manter o separador (e a página que aloja) aberto
`ue_ready ( )` | O componente terminou o carregamento; tudo o que foi enviado antes foi reproduzido
`ue_runtime_missing ( )` | O runtime WebView2 está ausente: o componente permanece vazio
`ue_bg_color (long al_color)` | O componente calculou a cor de fundo do respetivo tema; o userobject já a adotou (`backcolor`)

## Exemplos #

### Um dossiê de páginas de negócio #

    // event open : os userobjects de pagina ja estao colocados na janela
    uo_onglets.of_set_redraw(false)

    uo_onglets.of_add_page(/*id*/ "identite", /*titulo*/ "Identidade",  /*pagina*/ uo_identite)
    uo_onglets.of_add_page(/*id*/ "adresses", /*titulo*/ "Moradas",  /*pagina*/ uo_adresses)
    uo_onglets.of_add_page(/*id*/ "compta",   /*titulo*/ "Contabilidade", /*pagina*/ uo_compta)

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

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

### Documentos que se podem fechar, abertos a pedido #

    // Abrir um documento num novo separador que se pode fechar
    uo_onglets.of_add_page(/*id*/ ls_cle, /*titulo*/ ls_libelle, /*pagina*/ uo_editeur, &
                           /*fechavel*/ true, /*icone*/ "mono:img\doc.svg")
    uo_onglets.of_select_page(ls_cle)

    // event ue_tab_closed de uo_onglets : (string as_id)
    // O separador ja foi retirado ; falta libertar os dados de negocio.
    of_liberer_document(as_id)

### Reordenação com o rato #

    uo_onglets.ib_reorderable = true

    // event ue_tab_reordered de uo_onglets : (string as_id, integer ai_index)
    // ai_index = nova posicao, a partir de 1 : memoriza-se a ordem escolhida.
    of_enregistrer_ordre(as_id, ai_index)

### Faixa vertical #

    // Banda de separadores do lado esquerdo, util quando as etiquetas sao longas
    uo_onglets.is_position = uo_onglets.POSITION_START

### Inativar ou ocultar um separador consoante os direitos #

    // O caminho passa sempre pelo handle da pagina
    uo_onglets.of_item("compta").ib_enabled = of_a_le_droit("compta")
    uo_onglets.of_item("audit").ib_visible  = gb_mode_expert

### Reagir à seleção #

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

### Recusar uma mudança de separador ou um fecho #

    // As duas perguntas sao colocadas por PREDEFINICAO: nada a ativar. Estas linhas
    // fazem o contrario, eliminam-nas quando arbitrar nao serve.
    uo_onglets.ib_veto_selection = false
    uo_onglets.ib_veto_close     = false

    // event ue_selection_changing de uo_onglets : (string as_from_id, string as_id)
    // Devolver FALSE mantem o utilizador na pagina que abandona.
    if of_page_modifiee(as_from_id) then
        if MessageBox("Modificações", "Sair sem guardar?", Question!, YesNo!) = 2 then
            return false
        end if
    end if
    return true

    // event ue_tab_closing de uo_onglets : (string as_id)
    // Devolver FALSE mantem o separador e a pagina que aloja abertos.
    return not of_page_modifiee(as_id)

### Recomeçar com uma faixa vazia #

    // of_reset devolve CADA pagina alojada a sua janela de origem antes de esvaziar
    uo_onglets.of_reset()
    uo_onglets.of_add_page("accueil", "Início", uo_accueil)

## Boas práticas #

- Os controlos alojados têm de existir antes da chamada: devem ser colocados na janela em tempo de desenho, cabendo ao componente ocultá-los e reposicioná-los.
- Recomenda-se carregar as páginas uma única vez no `open` e comandar em seguida apenas a seleção: é mais rápido do que acrescentar e retirar páginas continuamente.
- Deve chamar-se `of_preload_icons` no arranque se os separadores tiverem ícones: sem isso, a primeira abertura de um separador oculto apresenta um breve atraso antes do ícone.
- As duas perguntas são colocadas por predefinição: um evento deixado vazio autoriza sempre a ação, não tem nada a fazer. Desative-as com `ib_veto_selection` / `ib_veto_close` a `false` se arbitrar não lhe serve, porque cada pergunta custa uma ida e volta ao PowerBuilder.
- Um controlo alojado é uma janela nativa: desenha-se por cima da camada web. Nenhum efeito visual do componente pode passar à frente dele.
- Não esquecer `of_reset()` antes de reconstruir uma faixa: sem essa chamada, a reutilização de um identificador de página falha e os controlos anteriores permanecem visíveis.
- Para zonas redimensionáveis e destacáveis em vez de páginas exclusivas, é preferível utilizar dockcontainer.

← Referência dos componentes · Índice do guia