# ribbon — `u_pbt_ribbon` #

← Component reference · Guide contents

> Office-style ribbon: tabs, groups, twelve types of rich controls, application menu, quick access toolbar, banner-headed contextual tabs and keytips.

> ▶ See it live — Demo application, Ribbon tile: the preview, the code behind it and this page, side by side.

## At a glance #

Userobject | `u_pbt_ribbon`
Item classes | `n_pbt_ribbon_tab` (tab), `n_pbt_ribbon_group` (group), `n_pbt_ribbon_item` (control), `n_pbt_ribbon_menu_item` (menu entry), `n_pbt_ribbon_ctx_group` (contextual tab group)
Used for | Replacing a menu bar and its toolbars with a modern command interface that is readable and clearly structured
Height | Intrinsic: the ribbon always fits itself to its content, nothing to enable — see Automatic height
Demo mode limit | 2 tabs maximum — see demo mode

### The golden rule: everything goes through the path #

The ribbon is a four-level hierarchy: tab → group → control → menu entry. There are no global identifiers for you to manage: every object is reached through the path that leads to it, and every addition is made on the parent handle.

    // Read or drive a control : the full path, always
    uo_ruban.of_tab("accueil").of_group("presse_papier").of_item("coller").ib_enabled = false

Two practical, comfortable consequences: two different groups can use the same control identifier without clashing, and events hand you the full path — you always know where the click came from.

## Quick start #

    // open event of the window
    n_pbt_ribbon_tab   lnv_onglet
    n_pbt_ribbon_group lnv_groupe

    lnv_onglet = uo_ruban.of_add_tab("accueil", "Home")

    lnv_groupe = lnv_onglet.of_add_group("presse_papier", "Clipboard")
    lnv_groupe.of_add_big_button("coller", "Paste", "mono:img\paste.svg")
    lnv_groupe.of_add_button("couper", "Cut", "mono:img\cut.svg")
    lnv_groupe.of_add_button("copier", "Copy", "mono:img\copy.svg")

    uo_ruban.of_select_tab("accueil")

    // ue_clicked event of uo_ruban : (string as_tab, string as_group, string as_id)
    choose case as_id
        case "coller" ; of_coller()
        case "couper" ; of_couper()
        case "copier" ; of_copier()
    end choose

## Properties #

Property | Type | Default | Purpose
--- | --- | --- | ---
`is_app_button` | string | `""` | Label of the application button, at the top left, which opens the application menu. Empty = the label `File`
`ib_minimized` | boolean | `false` | `true` collapses the ribbon down to its tab headers alone; clicking a tab unfolds it temporarily (raises `ue_minimized`)
`ib_veto_gallery` | boolean | `true` | Ask before a gallery tile is picked (raises `ue_gallery_selection_changing`, which can refuse). On by default: scripting nothing always lets the pick happen. Set it to `false` to drop the round trip to PowerBuilder (~35 ms) where it would show
`is_theme_style` | string | `fluent` | Visual style of the component (`THEME_STYLE_*` constants)
`is_theme_mode` | string | `light` | Light or dark variant (`THEME_MODE_*` constants)
`il_theme_accent` | long | `-1` | Accent color of this component (`-1` = the theme accent)
`is_tooltip` | string | `""` | Simple tooltip shown when hovering the component
`is_super_tooltip_title` | string | `""` | Title of the rich tooltip (takes precedence over `is_tooltip`)
`is_super_tooltip_text` | string | `""` | Text of the rich tooltip (rich markup accepted)
`is_super_tooltip_image` | string | `""` | Image of the rich tooltip

Constant — `ACCENT_LIGHT` (`-1`): pass it as the color of a contextual tab or group so that it follows the theme accent, lightened, instead of a fixed color.

### Properties of a tab — `n_pbt_ribbon_tab` #

Obtained through `of_tab(id)`, they can be changed on the fly.

Property | Type | Default | Purpose
--- | --- | --- | ---
`ib_visible` | boolean | `true` | `false` hides the tab without removing it — the very mechanism behind contextual tabs
`is_keytip` | string | `""` | Access letter displayed after pressing Alt (`"H"` for Home)

### Properties of a group — `n_pbt_ribbon_group` #

Obtained through `of_tab(id).of_group(id)`.

Property | Type | Default | Purpose
--- | --- | --- | ---
`ib_visible` | boolean | `true` | `false` hides the group and all its controls
`ib_launcher` | boolean | `false` | Displays the small arrow at the bottom right of the group — the dialog launcher (raises `ue_launcher`). The arrow lives in the group title bar, and a group the ribbon had to fold for lack of room keeps it in the very same place — it also travels into the panel the folded group opens.

Color picker mode constants: `COLORMODE_PALETTE` (swatch palette, the default mode) and `COLORMODE_OPEN` (full palette with confirmation).

### Properties of a control — `n_pbt_ribbon_item` #

Obtained through `of_tab(id).of_group(id).of_item(id)`. They apply to every type of control; the properties that make no sense for a given type are simply ignored.

Property | Type | Default | Purpose
--- | --- | --- | ---
`is_label` | string | `""` | Label of the control. Accepts rich text markup
`ib_enabled` | boolean | `true` | `false` grays out the control and blocks its activation
`ib_checked` | boolean | `false` | Pressed state of a toggle, or checked state of a check box
`ib_visible` | boolean | `true` | `false` hides the control; its neighbors close the gap
`is_text` | string | `""` | Text typed or selected in a drop-down
`id_value` | double | `0` | Numeric value of a spinner
`il_color` | long | `-1` | Current color of a color picker
`ii_visible_items` | integer | `3` | Gallery only: number of tiles the collapsed strip shows at once. The others stay reachable through the arrows, or in the expanded grid
`is_keytip` | string | `""` | Access letter of the control, displayed after pressing Alt
`is_tooltip` | string | `""` | Simple tooltip shown when hovering the item
`is_super_tooltip_title` | string | `""` | Title of the item rich tooltip (takes precedence over `is_tooltip`)
`is_super_tooltip_text` | string | `""` | Text of the item rich tooltip (rich markup accepted)
`is_super_tooltip_image` | string | `""` | Image of the item rich tooltip

### Properties of a menu entry — `n_pbt_ribbon_menu_item` #

Obtained through `of_tab(id).of_group(id).of_item(id).of_menu_item(id)`.

Property | Type | Default | Purpose
--- | --- | --- | ---
`ib_enabled` | boolean | `true` | `false` grays out the entry
`ib_checked` | boolean | `false` | Check mark of an entry created by `of_add_menu_check`

## The twelve types of controls #

They are all added on a group handle and return the handle of the control they create — so you can go straight on to filling a menu or a list.

Group method | Control obtained | Event
--- | --- | ---
`of_add_big_button (id, label, image)` | Full-height big button, icon above the label | `ue_clicked`
`of_add_big_split (id, label, image)` | Big split button: the upper part acts, the arrow opens the menu | `ue_clicked` · `ue_menu_selected`
`of_add_big_dropdown (id, label, image)` | Big button with a drop-down menu | `ue_menu_selected`
`of_add_button (id, label, image)` | Small button (stacked in columns of three) | `ue_clicked`
`of_add_toggle (id, label, image)` | Small toggle that stays pressed | `ue_toggled`
`of_add_dropdown (id, label, image)` | Small button with a drop-down menu | `ue_menu_selected`
`of_add_checkbox (id, label)` | Check box | `ue_toggled`
`of_add_separator ( )` | Vertical separator between two blocks of controls | —
`of_add_combo (id, largeur_px, modifiable)` | Drop-down, editable or not | `ue_combo_changed`
`of_add_spinner (id, largeur_px, mini, maxi, pas, valeur)` | Numeric spinner with arrows | `ue_value_changed`
`of_add_colorpicker (id, label, image, couleur)` | Split color button: clicking reapplies, the arrow opens the swatch palette | `ue_clicked` · `ue_color_changed`
`of_add_gallery (id, width_px, tile_width, tile_height)` | Scrolling strip of illustrated tiles | `ue_gallery_selection_changed`

`of_add_colorpicker` accepts a fifth argument `as_mode`: `COLORMODE_PALETTE` (swatch palette) or `COLORMODE_OPEN` (full palette with OK and Cancel buttons).

## Methods #

### Building the ribbon #

Method | Purpose
--- | ---
`of_add_tab (string as_id, string as_title)` | Adds a tab and returns its handle: chain the groups onto it
`of_insert_tab (string as_id, string as_title, integer ai_index)` | Adds a tab at the position you choose (`0` = first) instead of at the end, and returns its handle like `of_add_tab`. An id already taken is refused
`of_tab (string as_id)` | Handle of an existing tab (created on first access)
`of_add_group (string as_id, string as_title)` | On a tab handle: adds a titled group and returns its handle
`of_group (string as_id)` | On a tab handle: handle of an existing group
`of_item (string as_id)` | On a group handle: handle of an existing control
`of_select_tab (string as_id)` | Activates a tab — strictly equivalent to a user click (raises `ue_selection_changed`)
`of_selected_key ( )` | Identifier of the active tab, `""` if none. Kept up to date by `ue_selection_changed`, so it is right from start-up
`of_remove_group (string as_id)` | On a tab handle: removes a group and all its controls
`of_remove_item (string as_id)` | On a group handle: removes a control
`of_remove_tab (string as_id)` | Removes a tab and all its content
`of_clear ( )` | Empties the ribbon completely: tabs, groups, controls, quick access toolbar, application menu

### Filling menus and lists #

All these methods are called on a control handle.

Method | Purpose
--- | ---
`of_add_menu_item (string as_id, string as_label, string as_image)` | Root entry in the menu of a drop-down or split button
`of_add_menu_item (string as_parent, string as_id, string as_label, string as_image)` | Cascading entry: `as_parent` designates an existing entry of that menu
`of_add_menu_check (string as_id, string as_label)` · `(id, label, image)` | Checkable entry: clicking toggles its state and reports it in `ue_menu_selected`
`of_add_menu_header (string as_label)` | Non-clickable title line, to break up a long menu
`of_add_menu_separator ( )` | Separator line in the menu
`of_menu_item (string as_id)` | Handle of a menu entry, to gray it out or check it on the fly
`of_add_combo_item (string as_label)` | Adds a choice to the list of a drop-down
`of_add_gallery_item (string as_id, string as_image, string as_label)` | Adds a tile to a gallery
`of_select_item (string as_id)` | Selects a gallery tile programmatically (without raising an event)
`of_open ( )` | Programmatically opens the menu, the list or the swatch palette of the control

### Application menu and quick access toolbar #

Method | Purpose
--- | ---
`of_add_app_menu_item (string as_id, string as_label, string as_image)` | Root entry of the application menu (the one opened by the `is_app_button` button)
`of_add_app_menu_item (string as_parent, string as_id, string as_label, string as_image)` | Cascading entry of the application menu
`of_add_app_menu_separator (string as_id)` | Separator line in the application menu
`of_add_qat (string as_id, string as_image, string as_tooltip)` | Button of the quick access toolbar, above the tabs, and returns its handle
`of_qat_item (string as_id)` | Handle of a quick access button, to gray it out or hide it on the fly

### Contextual tabs #

Method | Purpose
--- | ---
`of_add_contextual_tab (string as_id, string as_title, long al_color)` | Standalone contextual tab: created hidden, marked with a colored edge. Pass `ACCENT_LIGHT` to follow the theme accent
`of_add_contextual_group (string as_id, string as_title)` · `(id, title, al_color)` | Group of contextual tabs: a colored titled banner sits above its tabs. Returns its handle
`of_ctx_group (string as_id)` | Retrieves the handle of a contextual group that already exists — handy when you did not keep the one returned at creation
`of_add_tab (string as_id, string as_title)` | On a contextual group handle: adds a tab topped by the banner, created hidden
`of_tab (string as_id)` | On a contextual group handle: handle of one of its tabs
`il_color` (property) | On a contextual group handle: recolors the banner on the fly (`ACCENT_LIGHT` to go back to the accent)

### Shared #

Method | Purpose
--- | ---
`of_reset ( )` | Empties the ribbon and returns it to its brand-new state, properties included
`of_set_redraw (boolean)` | Groups a burst of changes into a single rendering
`of_preload_icons (string as_icons[])` | Warms up a batch of icons at startup: a tab opened later displays its own instantly
`of_save_as_png (string)` · `of_save_as_jpg (string)` | Exports the rendering as an image

## Events #

Every control event carries the full path: you never need an identifier that is unique across the whole application.

Event | Raised when
--- | ---
`ue_clicked (string as_tab, string as_group, string as_id)` | A button, a big button, the main part of a split button or of a color picker is clicked
`ue_toggled (string as_tab, string as_group, string as_id, boolean ab_checked)` | A toggle or a check box changes state; `ab_checked` carries the new state
`ue_menu_selected (string as_tab, string as_group, string as_dropdown, string as_id, boolean ab_checked)` | An entry of a drop-down menu is chosen. `as_dropdown` = the owning control, `as_id` = the entry, `ab_checked` = the state of a checkable entry
`ue_combo_changed (string as_tab, string as_group, string as_id, string as_text)` | A drop-down changes value, either by selection or by typing
`ue_value_changed (string as_tab, string as_group, string as_id, double ad_value)` | A numeric spinner changes value
`ue_color_changed (string as_tab, string as_group, string as_id, long al_color)` | A color is chosen in a color picker
`ue_gallery_selection_changed (string as_tab, string as_group, string as_id, string as_from_item, string as_item)` | A gallery tile has been picked. Same arguments as `ue_gallery_selection_changing`: the question and its outcome read the same way, and `as_from_item` is the tile left
`ue_gallery_selection_changing (string as_tab, string as_group, string as_id, string as_from_item, string as_item)` → boolean | Cancelable, raised before the tile is picked. Raised by default; `ib_veto_gallery = false` removes it. `as_from_item` is the current tile. Return `false` to keep it (a style the document cannot take yet)
`ue_launcher (string as_tab, string as_group)` | The launcher arrow of a group is clicked — open your options window
`ue_selection_changed (string as_id)` | A tab becomes active, by a click or through `of_select_tab`
`ue_app_button ( )` | The application button is clicked
`ue_app_menu_selected (string as_id)` | An entry of the application menu is chosen
`ue_minimized (boolean ab_minimized)` | The ribbon is collapsed or expanded by the user
`ue_size_changed (long al_height, boolean ab_minimized)` | The ribbon's height changed on its own: folded, unfolded, a contextual tab shown, a narrower window dropping a row. Unlike `ue_auto_height` — which only speaks when the component sizes itself — this one fires whether auto-height is on or off: it is pure information, for laying out what sits underneath
`ue_keytips (boolean ab_on, integer ai_level)` | The keytips appear (`true`) or disappear (`false`). `ai_level` tells how far the navigation has gone: `1` = the tabs are lettered, `2` = the commands of the current tab are, `0` = no keytip left
`ue_auto_height (long al_height)` | The ribbon reports its ideal height and has just adjusted to it — always active: a ribbon's height is intrinsic
`ue_ready ( )` | The component has finished loading; everything sent beforehand has been replayed
`ue_runtime_missing ( )` | The WebView2 runtime is missing: the component stays empty
`ue_bg_color (long al_color)` | The component has computed its theme background color; the userobject has already adopted it (`backcolor`)

## Examples #

### A complete ribbon, from the application menu to the groups #

    // open event : the whole build is grouped into a single rendering
    n_pbt_ribbon_tab   lnv_onglet
    n_pbt_ribbon_group lnv_groupe
    n_pbt_ribbon_item  lnv_item

    uo_ruban.of_set_redraw(false)
    uo_ruban.is_app_button = "File"

    // Application menu, with a cascade under "Save as"
    uo_ruban.of_add_app_menu_item(/*id*/ "nouveau", /*label*/ "New", /*image*/ "mono:img\new.svg")
    uo_ruban.of_add_app_menu_item("ouvrir", "Open...", "mono:img\open.svg")
    uo_ruban.of_add_app_menu_item("enr_sous", "Save as", "mono:img\saveas.svg")
    uo_ruban.of_add_app_menu_item(/*parent*/ "enr_sous", /*id*/ "sous_pdf", "PDF document", "")
    uo_ruban.of_add_app_menu_item(/*parent*/ "enr_sous", /*id*/ "sous_csv", "CSV file", "")
    uo_ruban.of_add_app_menu_separator("sep1")
    uo_ruban.of_add_app_menu_item("quitter", "Exit", "mono:img\exit.svg")

    // Quick access toolbar, above the tabs
    uo_ruban.of_add_qat(/*id*/ "qat_enr", /*image*/ "mono:img\save.svg", /*tooltip*/ "Save")
    uo_ruban.of_add_qat("qat_annuler", "mono:img\undo.svg", "Undo")

    // Home tab
    lnv_onglet = uo_ruban.of_add_tab("accueil", "Home")
    lnv_onglet.is_keytip = "H"

    lnv_groupe = lnv_onglet.of_add_group("presse_papier", "Clipboard")
    lnv_item = lnv_groupe.of_add_big_split("coller", "Paste", "mono:img\paste.svg")
    lnv_item.of_add_menu_item("coller_texte", "Paste without formatting", "")
    lnv_item.of_add_menu_item("coller_lien", "Paste as link", "")
    lnv_groupe.of_add_button("couper", "Cut", "mono:img\cut.svg")
    lnv_groupe.of_add_button("copier", "Copy", "mono:img\copy.svg")
    lnv_groupe.ib_launcher = true       // options arrow at the bottom right

    uo_ruban.of_set_redraw(true)
    uo_ruban.of_select_tab("accueil")

### A single click router #

    // ue_clicked event of uo_ruban : (string as_tab, string as_group, string as_id)
    // The full path arrives with the event : a single router is enough,
    // and two groups can reuse the same identifier without getting in the way.
    choose case as_group + "/" + as_id
        case "presse_papier/couper" ; of_couper()
        case "presse_papier/copier" ; of_copier()
        case "presse_papier/coller" ; of_coller()
        case "police/gras"          ; of_basculer_gras()
    end choose

### Driving the state of controls according to permissions #

    // Always through the path : tab > group > control
    uo_ruban.of_tab("accueil").of_group("presse_papier").of_item("coller").ib_enabled = of_presse_papier_non_vide()
    uo_ruban.of_tab("accueil").of_group("outils").of_item("pinceau").ib_checked = true
    uo_ruban.of_tab("admin").ib_visible = gb_administrateur

    // Gray out an entry INSIDE a drop-down menu (level 4)
    uo_ruban.of_tab("accueil").of_group("presse_papier").of_item("coller") &
            .of_menu_item("coller_lien").ib_enabled = false

### Banner-headed contextual tabs #

The Office principle: tabs that appear only when the selection warrants them, topped by a colored titled banner.

    // open event : we prepare the contextual group, hidden by default
    n_pbt_ribbon_ctx_group lnv_ctx
    n_pbt_ribbon_tab       lnv_onglet
    n_pbt_ribbon_group     lnv_groupe

    // Without a color, the banner follows the theme accent ; RGB(...) to force it
    lnv_ctx = uo_ruban.of_add_contextual_group(/*id*/ "img", /*title*/ "Picture Tools", &
                                               /*color*/ RGB(224, 32, 96))
    lnv_onglet = lnv_ctx.of_add_tab("format", "Format")
    lnv_groupe = lnv_onglet.of_add_group("ajuster", "Adjust")
    lnv_groupe.of_add_big_button("rogner", "Crop", "mono:img\crop.svg")
    lnv_groupe.of_add_button("pivoter", "Rotate", "mono:img\rotate.svg")

    // When a picture is selected : we reveal the tab and activate it
    uo_ruban.of_tab("format").ib_visible = true
    uo_ruban.of_select_tab("format")

    // When it is deselected : we hide it, the banner goes away with it
    uo_ruban.of_tab("format").ib_visible = false

### Drop-down, spinner, color picker and gallery #

    n_pbt_ribbon_group lnv_groupe
    n_pbt_ribbon_item  lnv_item

    lnv_groupe = uo_ruban.of_tab("accueil").of_add_group("police", "Font")

    // Editable drop-down : we fill it through its handle
    lnv_item = lnv_groupe.of_add_combo(/*id*/ "nom_police", /*width_px*/ 140, /*editable*/ true)
    lnv_item.of_add_combo_item("Segoe UI")
    lnv_item.of_add_combo_item("Arial")
    lnv_item.of_add_combo_item("Calibri")
    lnv_item.is_text = "Segoe UI"

    // Numeric spinner : min, max, step, initial value
    lnv_groupe.of_add_spinner(/*id*/ "taille", /*width_px*/ 70, /*min*/ 6, /*max*/ 96, &
                              /*step*/ 1, /*value*/ 11)

    // Color picker in full palette mode (with OK / Cancel)
    lnv_groupe.of_add_colorpicker(/*id*/ "couleur", /*label*/ "Color", &
                                  /*image*/ "mono:img\font-color.svg", &
                                  /*color*/ RGB(0, 0, 0), &
                                  /*mode*/ lnv_groupe.COLORMODE_OPEN)

    // Style gallery : scrolling illustrated tiles
    lnv_item = lnv_groupe.of_add_gallery(/*id*/ "styles", /*width_px*/ 220, &
                                         /*tile_width*/ 64, /*tile_height*/ 48)
    lnv_item.of_add_gallery_item("st_normal", "img\style-normal.png", "Normal")
    lnv_item.of_add_gallery_item("st_titre",  "img\style-titre.png",  "Title")
    lnv_item.of_add_gallery_item("st_note",   "img\style-note.png",   "Note")
    lnv_item.of_add_gallery_item("st_code",   "img\style-code.png",   "Code")

    // The collapsed strip shows 4 thumbnails at a time ; the others stay
    // reachable through the arrows, or in the expanded grid.
    lnv_item.ii_visible_items = 4

    lnv_item.of_select_item("st_normal")

    // ue_value_changed event of uo_ruban : (string as_tab, string as_group, string as_id, double ad_value)
    if as_id = "taille" then of_appliquer_taille(ad_value)

    // ue_color_changed event of uo_ruban : (string as_tab, string as_group, string as_id, long al_color)
    if as_id = "couleur" then of_appliquer_couleur(al_color)

### Refusing the choice of a gallery thumbnail #

    // The question is asked by DEFAULT: nothing to enable. This line does the
    // opposite, removing it where arbitration is useless.
    uo_ruban.ib_veto_gallery = false

    // ue_gallery_selection_changing event of uo_ruban :
    //   (string as_tab, string as_group, string as_id, string as_from_item, string as_item)
    // Returning FALSE keeps the current thumbnail (as_from_item).
    if as_item = "st_code" and not of_document_supporte_code() then
        MessageBox("Style", "This document cannot take the Code style.")
        return false
    end if
    return true

### The dialog launcher #

    // The small arrow at the bottom right of the group
    uo_ruban.of_tab("accueil").of_group("police").ib_launcher = true

    // ue_launcher event of uo_ruban : (string as_tab, string as_group)
    // The path identifies the group : we open the matching options window.
    choose case as_group
        case "police"        ; open(w_options_police)
        case "presse_papier" ; open(w_options_collage)
    end choose

### Keytips: driving the ribbon from the keyboard #

    // Alt displays the letters ; Alt then H then C triggers "copier"
    uo_ruban.of_tab("accueil").is_keytip = "H"
    uo_ruban.of_tab("accueil").of_group("presse_papier").of_item("copier").is_keytip = "C"
    uo_ruban.of_tab("accueil").of_group("presse_papier").of_item("couper").is_keytip = "X"

Pressing Alt wherever the focus sits in the window hands over to the ribbon: it takes the keyboard focus and raises its letters. You have nothing to wire — it is enough that at least one keytip is declared. Escape, a second Alt or picking a command return the focus to the control the user had left.

`ue_keytips` warns you at every state change, and gives you the current level:

    // ue_keytips event of uo_ruban : (boolean ab_on, integer ai_level)
    // The keyboard is driving the ribbon: clear the status bar hint, which talks
    // about the mouse, and put it back when the letters drop.
    if ab_on then
        uo_statut.of_item("main").is_text = "Type a letter (level " + String(ai_level) + ")"
    else
        uo_statut.of_item("main").is_text = ""
    end if

### Rich tooltips on a control #

    n_pbt_ribbon_item lnv_item

    lnv_item = uo_ruban.of_tab("accueil").of_group("presse_papier").of_item("coller")
    lnv_item.is_super_tooltip_title = "Paste (Ctrl+V)"
    lnv_item.is_super_tooltip_text  = "Inserts the contents of the clipboard." &
                                    + "[br][br][size-=15]Use the arrow to paste without formatting.[/size-=15]"
    lnv_item.is_super_tooltip_image = "img\paste.png"

These four properties are exactly the same as on any other item of the library. For a one-line tooltip, `is_tooltip` alone is enough.

### Automatic height #

The ribbon sizes itself: there is nothing to enable. It notifies you on every height change (collapsing, contextual tab, theme change) so that you can reposition whatever sits below.

    // ue_auto_height event of uo_ruban : (long al_height)
    // The ribbon has already resized itself : we reposition what sits below.
    uo_contenu.y      = uo_ruban.y + uo_ruban.height
    uo_contenu.height = this.height - uo_contenu.y

### Collapsing the ribbon to save space #

    uo_ruban.ib_minimized = true

    // ue_minimized event of uo_ruban : (boolean ab_minimized)
    // We remember the user preference for the next time the window opens.
    of_enregistrer_preference("ruban_replie", ab_minimized)

### Starting over from an empty ribbon #

    // of_reset empties tabs, groups, controls, quick access toolbar and menu
    uo_ruban.of_reset()
    uo_ruban.of_add_tab("accueil", "Home")

## From an existing PowerBuilder menu #

A PowerBuilder application has already described its commands once: in its menu. Labels, shortcuts, images, separators, sub-menus, tooltips — it is all there. `n_pbt_menu2ribbon` reads that menu back through RTTI and writes the PowerScript that builds the matching ribbon.

    // Once, by hand : the generator WRITES code, it does not run in production.
    // Paste its result into the open event of your window.
    n_pbt_menu2ribbon lnv_gen
    string ls_code

    lnv_gen = create n_pbt_menu2ribbon
    ls_code = lnv_gen.of_generate(/*menu*/ m_principal, /*variable*/ "uo_ruban")
    destroy lnv_gen

    ClipBoard(ls_code)

The conversion is deterministic: no AI, no network call, nothing that leaves the machine. And the generated identifiers keep the menu path (`m_principal.m_fichier.m_ouvrir`), so the `choose case` of your existing actions works as it is from `ue_clicked`.

The generated code is a starting point to review, not a deliverable: a menu is a list, a ribbon is a layout. Group things, choose your big buttons, drop what does not deserve to be permanently on show. Sample 9 of the ribbon page in the demo application shows a complete result.

## Best practices #

- Wrap the whole build in `of_set_redraw(false)` / `of_set_redraw(true)`: a complete ribbon is then drawn in one go, without flickering.
- Keep the returned handles (`n_pbt_ribbon_tab`, `n_pbt_ribbon_group`, `n_pbt_ribbon_item`) in local variables for the duration of the build: the code stays short and readable. To drive a control later on, simply go back through the path.
- Call `of_preload_icons` at startup: without it, the first display of a tab that has never been opened shows a brief delay before its icons appear.
- An identifier only needs to be unique within its group. Take advantage of that to name your controls simply (`copier`, `coller`) rather than with prefixes.
- Reserve contextual tabs for commands that make no sense out of context: a grayed-out permanent tab is more restful than a tab that appears and disappears.
- The question is asked by default: a `ue_gallery_selection_changing` left empty always allows the pick, you have nothing to do. Switch it off with `ib_veto_gallery = false` if arbitration is useless to you, since every question costs a round trip to PowerBuilder.
- One dialog launcher per group at most, and only if the group really has advanced options to offer.
- To rebuild an entire ribbon, prefer `of_clear()` (or `of_reset()`) over a series of `of_remove_tab` calls: without that, reusing a tab identifier fails.
- For a lighter command bar, without tabs or groups, see toolbar; for side navigation, listbar.

← Component reference · Guide contents