# listbar — `u_pbt_listbar` #

← Referência dos componentes · Índice do guia

> Barra de navegação lateral: secções recolhíveis em acordeão que contêm entradas com ícone, com faixa de destaque na entrada atual e recolha para uma calha de ícones.

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

## Em resumo #

Userobject | `u_pbt_listbar`
Classe de items | `n_pbt_listbar_section` (secção) → `n_pbt_listbar_item` (entrada)
Serve para | Substituir um menu lateral improvisado à custa de botões por uma navegação estruturada, com tema e recolhível
Opções opt-in | `ib_auto_width`, `ib_reorderable`

É o único componente da biblioteca que publica `ib_auto_width`: a sua largura natural tem significado, uma vez que a calha recolhida é nitidamente mais estreita do que a barra expandida. Aliás, o caso corrente já está coberto sem ativar nada — `ib_collapsed = true` encolhe a barra até à calha e devolve-lhe a largura ao expandir.

## Início rápido #

    // event open da janela
    n_pbt_listbar_section lnv_section

    lnv_section = uo_nav.of_add_header(/*id*/ "nav", /*titulo*/ "Navegacao")
    lnv_section.of_add_item(/*id*/ "accueil",   /*etiqueta*/ "Inicio",    /*icone*/ "mono:img\home.svg")
    lnv_section.of_add_item(/*id*/ "documents", /*etiqueta*/ "Documentos", /*icone*/ "mono:img\doc.svg")
    lnv_section.of_add_item(/*id*/ "recherche", /*etiqueta*/ "Pesquisa",  /*icone*/ "mono:img\find.svg")

    uo_nav.of_add_header(/*id*/ "config", /*titulo*/ "Definicoes") &
          .of_add_item(/*id*/ "preferences", /*etiqueta*/ "Preferencias", /*icone*/ "mono:img\gear.svg")

    // A selecao coloca-se na ENTRADA, atraves do seu caminho completo
    uo_nav.of_select_item("nav", "accueil")

    // event ue_selection_changed de uo_nav: (string as_from_section, string as_from_id,
    //                                        string as_section, string as_id)
    choose case as_section + "/" + as_id
        case "nav/accueil"       ; of_ouvrir_accueil()
        case "nav/documents"     ; of_ouvrir_documents()
        case "config/preferences"; of_ouvrir_preferences()
    end choose

## Dois níveis, um caminho obrigatório #

Um identificador de entrada só é único dentro da respetiva secção: não existe, portanto, qualquer atalho para a entrada. Todo o acesso passa pela secção, o que torna a escrita inequívoca — ver Hierarquias.

    // Componente  ->  seccao         ->  entrada     ->  propriedade
    uo_nav        .of_section("nav") .of_item("docs") .is_text = "Documents"

Os eventos incluem igualmente o caminho completo, e com ele a entrada abandonada: `ue_selection_changed(as_from_section, as_from_id, as_section, as_id)`.

## Propriedades #

Propriedade | Tipo | Predefinição | Função
--- | --- | --- | ---
`ib_collapsed` | boolean | `false` | `true` recolhe a barra numa calha de ícones: as etiquetas desaparecem, os ícones continuam clicáveis
`ib_auto_width` | boolean | `false` | Opt-in: o mesmo para a largura, incluindo expandida (a barra ajusta-se à etiqueta mais comprida). Quanto ao recolhimento em calha, esse já encolhe sozinho; `ue_auto_width` segue-se em ambos os casos
`ib_reorderable` | boolean | `false` | Opt-in: o utilizador pode deslocar uma entrada com o rato. O deslocamento mantém-se dentro da sua secção — um identificador de entrada só é único aí, pelo que atravessar arriscaria duas chaves idênticas (aciona `ue_item_reordered`)
`ib_veto_selection` | boolean | `true` | Perguntar antes de a seleção se deslocar (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 — navegação por teclado, seleção deslocada em ciclo
`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 secção — `n_pbt_listbar_section` #

Propriedade | Tipo | Predefinição | Função
--- | --- | --- | ---
`is_title` | string | `""` | Título da secção. Aceita o texto formatado com etiquetas. Um título vazio não apresenta qualquer linha de cabeçalho: a secção passa a ser um simples agrupamento invisível
`ib_collapsed` | boolean | `false` | Acordeão: `true` recolhe as entradas desta secção. O cabeçalho permanece visível e o respetivo galão roda

### Propriedades de uma entrada — `n_pbt_listbar_item` #

Propriedade | Tipo | Predefinição | Função
--- | --- | --- | ---
`is_text` | string | `""` | Etiqueta da entrada, modificável em tempo real sem reconstruir a barra. Aceita o texto formatado com etiquetas
`is_image` | string | `""` | Ícone, modificável em tempo real (formas aceites: caminho, `mono:`, `tint:`, recurso de DLL)
`ib_enabled` | boolean | `true` | `false` esbate a entrada e bloqueia o respetivo clique
`ib_visible` | boolean | `true` | `false` oculta a entrada sem a remover da barra
`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 #

### No componente #

Método | Função
--- | ---
`of_add_header (string as_id, string as_text)` | Adiciona uma secção e devolve o respetivo handle `n_pbt_listbar_section`, no qual se encadeiam as adições de entradas
`of_section (string as_id)` | Handle de uma secção existente (criado no primeiro acesso)
`of_insert_item (string as_section, string as_id, string as_text, integer ai_index)` | Insere uma entrada na posição `ai_index` dentro da respetiva secção
`of_insert_item (string as_section, string as_id, string as_text, string as_image, integer ai_index)` | Idem, com o ícone da entrada: `of_add_item` aceita um, pelo que inserir também o deve poder aceitar
`of_move_item (string as_section, string as_id, integer ai_index)` | Desloca uma entrada existente dentro da sua secção, conservando o respetivo estado
`of_remove_item (string as_section, string as_id)` | Remove uma entrada designada pelo par secção / identificador
`of_clear ( )` | Esvazia a barra: todas as secções e todas as entradas
`of_select_item (string as_section, string as_id)` | Seleciona uma entrada — estritamente equivalente a um clique do utilizador: `ue_selection_changing` é perguntado primeiro, depois `ue_selection_changed` anuncia a mudança
`of_get_layout ( )` | Relê a disposição atual em JSON: as secções por ordem, cada uma com as suas entradas por ordem e o seu estado recolhido. 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_clear_selection ( )` | Não deixa nenhuma entrada selecionada. Anunciado como qualquer outra mudança
`of_selected_key ( )` | Identificador da entrada selecionada, `""` se não houver nenhuma. Sempre a atual: um clique e `of_select_item` passam ambos por `ue_selection_changed`
`of_selected_section ( )` | Secção que contém a entrada selecionada — um id de entrada só é único dentro da sua secção, por isso é o par que designa a seleção. `""` se não houver nenhuma
`of_reset ( )` | Esvazia a barra e repõe o componente no seu estado inicial
`of_set_redraw (boolean)` | Agrupa uma rajada de modificações numa única representação
`of_save_as_png (string)` · `of_save_as_jpg (string)` | Exporta a representação como imagem

### Numa secção — `n_pbt_listbar_section` #

Método | Função
--- | ---
`of_add_item (string as_id, string as_label, string as_icon_file)` | Adiciona uma entrada normal a esta secção e devolve o respetivo handle
`of_item (string as_id)` | Handle de uma entrada desta secção (criado no primeiro acesso)

## Eventos #

Evento | Acionado quando
--- | ---
`ue_selection_changed (string as_from_section, string as_from_id, string as_section, string as_id)` | A seleção deslocou-se — por clique ou através de `of_select_item`. Mesmos argumentos que `ue_selection_changing`: a pergunta e o seu resultado leem-se da mesma forma, e o par `as_from_*` designa a entrada abandonada (vazio se não houver nenhuma)
`ue_section_toggled (string as_section, boolean ab_collapsed)` | O utilizador recolhe ou expande uma secção através do respetivo cabeçalho
`ue_item_reordered (string as_section, string as_id, integer ai_index)` | O utilizador terminou de arrastar uma entrada. `ai_index` é a sua nova ordem dentro da secção, a partir de 1. Guarde essa ordem para devolver ao utilizador a barra tal como a deixou
`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_section, string as_from_id, string as_section, string as_id)` → boolean | Cancelável, colocado antes de a seleção se deslocar. Lançado por predefinição; `ib_veto_selection = false` elimina-o. Devolva `false` para manter o utilizador onde está
`ue_auto_width (long al_width)` | O componente recalculou a sua largura ideal — requer `ib_auto_width = true`
`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 #

### Menu lateral completo #

    n_pbt_listbar_section lnv_dossiers, lnv_outils

    uo_nav.of_set_redraw(false)

    lnv_dossiers = uo_nav.of_add_header("dossiers", "Processos")
    lnv_dossiers.of_add_item("recents",  "Recentes", "mono:img\clock.svg")
    lnv_dossiers.of_add_item("clients",  "Clientes", "mono:img\user.svg")
    lnv_dossiers.of_add_item("archives", "Arquivo",  "mono:img\box.svg")

    lnv_outils = uo_nav.of_add_header("outils", "Ferramentas")
    lnv_outils.of_add_item("import", "Importar", "mono:img\import.svg")
    lnv_outils.of_add_item("export", "Exportar", "mono:img\export.svg")

    uo_nav.of_set_redraw(true)
    uo_nav.of_select_item("dossiers", "recents")

### Calha recolhível que liberta espaço #

    // Recolher numa calha de icones: a barra encolhe por si propria (e retoma a sua
    // largura quando a expandir).
    uo_nav.ib_collapsed = true

    // event ue_auto_width de uo_nav: (long al_width)
    // A barra acaba de adotar a sua largura ideal: reposicionar o que esta a direita.
    uo_contenu.x     = uo_nav.x + al_width
    uo_contenu.width = parent.width - uo_contenu.x

### Acordeão: recolher uma secção #

    // Recolher a seccao do arquivo, raramente utilizada
    uo_nav.of_section("archives").ib_collapsed = true

    // event ue_section_toggled de uo_nav: (string as_section, boolean ab_collapsed)
    of_enregistrer_preference("section_" + as_section, String(ab_collapsed))

### Reagir à navegação #

    // event ue_selection_changed de uo_nav: (string as_from_section, string as_from_id,
    //                                        string as_section, string as_id)
    // A seccao faz parte da chave: duas seccoes podem ter uma entrada "liste".
    choose case as_section
        case "dossiers" ; of_ouvrir_dossier(as_id)
        case "outils"   ; of_lancer_outil(as_id)
    end choose

### Recusar uma mudança de seleção #

    // A pergunta e colocada por PREDEFINICAO: nada a ativar. Esta linha faz o
    // contrario, elimina-a quando arbitrar nao serve e o custo se notaria.
    uo_nav.ib_veto_selection = false

    // event ue_selection_changing de uo_nav:
    //   (string as_from_section, string as_from_id, string as_section, string as_id)
    // Devolver FALSE mantem o utilizador na entrada que abandona.
    if of_saisie_en_cours(as_from_section, as_from_id) then
        MessageBox("Introdução", "Termine a ficha em curso antes de navegar.")
        return false
    end if
    return true

### Atualizar uma entrada em tempo real #

    n_pbt_listbar_item lnv_entree

    // O caminho completo e obrigatorio: componente -> seccao -> entrada
    lnv_entree = uo_nav.of_section("dossiers").of_item("recents")
    lnv_entree.is_text  = "Recentes (12)"
    lnv_entree.is_image = "mono:img\clock_full.svg"

    // Esbater ou ocultar consoante os direitos, sem reconstruir a barra
    uo_nav.of_section("outils").of_item("import").ib_enabled = of_a_le_droit("import")
    uo_nav.of_section("outils").of_item("export").ib_visible = gb_mode_expert

### Seleção controlada a partir do código #

    // Colocar a selecao noutro sitio: a faixa de destaque acompanha
    uo_nav.of_select_item("dossiers", "clients")

    // Ou limpa-la completamente
    uo_nav.of_clear_selection()

### Reconstruir a barra #

    // of_clear esvazia seccoes e entradas; of_reset acrescenta as predefinicoes do componente
    uo_nav.of_clear()
    of_construire_menu_selon_profil()

## Boas práticas #

- Conserve numa variável local o handle de secção devolvido por `of_add_header`: é mais legível do que voltar a chamar `of_section(...)` em cada entrada.
- Utilize `mono:` para os ícones: recolorem-se com o tema, tanto claro como escuro, e permanecem legíveis depois de a barra ser recolhida em calha.
- Um título de secção vazio serve de agrupamento discreto: as entradas ficam espaçadas sem que apareça qualquer linha de cabeçalho.
- `ib_collapsed` encolhe o userobject até à calha, mas não desloca os seus outros controlos: trate `ue_auto_width` para ocupar o espaço libertado, caso contrário este permanece vazio.
- No modo calha subsistem apenas os ícones: indique um tooltip em cada entrada para que as etiquetas permaneçam acessíveis.
- A pergunta é colocada por predefinição: um `ue_selection_changing` deixado vazio autoriza sempre o deslocamento, não tem nada a fazer. Desative-a com `ib_veto_selection = false` onde o clique é repetido — navegação por teclado, seleção pilotada em ciclo — porque cada pergunta custa uma ida e volta ao PowerBuilder.
- `ib_enabled = false` é preferível a `ib_visible = false` quando a entrada voltará a ficar disponível: o menu não muda de forma diante dos olhos do utilizador.

← Referência dos componentes · Índice do guia