PBToolboxAI v1 ← Site

4. Themes and appearance #

← Shared foundation · Contents · Language and RTL →


4.1 Themes: two axes #

A theme is made of a style and a mode:

AxisValues
Style (is_theme_style)fluent · metro · office · office2007 · office2003
Mode (is_theme_mode)light · dark

That is ten themes, named <style>-<mode>: fluent-light, fluent-dark, office2007-light, metro-dark


Set the theme once for the whole application, before the first window opens. It is injected into every component before its first render: no flash of a light style on a dark application.

// open event of the application object
PBT_SetDefaultTheme("fluent-dark")
PBT_SetDefaultThemeAccent(RGB(0, 120, 212))   // optional

You can switch on the fly at any time: every component already open is re-themed instantly.

// Light / dark toggle from a button in the application
PBT_SetDefaultTheme("fluent-light")
FunctionEffect
PBT_SetDefaultTheme (string as_nom)Default theme of the process (broadcast to every component)
PBT_GetDefaultTheme ( ) → stringCurrent default theme
PBT_SetDefaultThemeAccent (long al_couleur)Default accent color (-1 = the theme's native accent)
PBT_GetDefaultThemeAccent ( ) → longCurrent default accent

4.3 The theme of one specific component #

A component can depart from the default theme:

uo_editeur.is_theme_style = uo_editeur.THEME_STYLE_OFFICE2007
uo_editeur.is_theme_mode  = uo_editeur.THEME_MODE_DARK
uo_editeur.il_theme_accent = RGB(200, 60, 40)     // -1 = the theme accent
PropertyTypeDefaultPurpose
is_theme_stylestringfluentVisual style (THEME_STYLE_* constants)
is_theme_modestringlightLight or dark variant (THEME_MODE_* constants)
il_theme_accentlong-1Accent color of this component (-1 = the theme accent)

An of_reset() brings the component back to the process default theme.

💡 A single theme for the whole application still looks best. Save local themes for special cases (a deliberately contrasted area, a theme preview).


4.4 Recolouring a component, a group or an item #

Three scopes, the same properties. Nothing to name, nothing to guess.

// The whole component
uo_ruban.il_theme_accent = RGB(0, 120, 90)

// A group: everything inside it follows
uo_ruban.of_tab("accueil").of_group("presse-papiers").il_accent = RGB(0, 120, 90)

// One item
uo_liste.of_item("supprimer").il_text_color = RGB(200, 70, 70)
uo_liste.of_item("supprimer").il_back_color = RGB(255, 235, 235)

// The same two, under the pointer
uo_liste.of_item("supprimer").il_back_color_hover = RGB(255, 220, 220)

// Back to the component's colour
uo_liste.of_item("supprimer").il_text_color = -1
PropertyWhereWhat it recolours
il_theme_accentthe componentits accent, and everything derived from it: the readable text on top, the application background, the tab underline
il_accentan item, group, tab or bar handlewhat that zone paints with the accent, descendants included
il_back_color · il_text_colorsamethe background and the text of the item
il_back_color_hover · il_text_color_hoversamethe same two, under the pointer

-1 restores the colour the component gives, which itself comes from the theme. An item colour survives the component being rebuilt: it is carried by a style rule targeting the item, not by a property set on the node of the moment. of_reset() clears everything.

il_accent only repaints what the zone paints with the accent — a selection, an active underline, a progress bar. A component that never uses it will show nothing: for "this entry in red", il_back_color and il_text_color are the right tools, read by every component with items.

The font of the whole application #

PBT_SetDefaultFont("Segoe UI", 14)

One call dresses every live component and the ones created afterwards — the font is injected before their first paint. An empty family or a size of 0 gives that half back to the theme.


4.5 The component background is reported back to PowerBuilder #

Every component paints its background according to the theme, then reports its color: the userobject adopts that color (backcolor) and raises ue_bg_color, so that the window and the neighboring PowerBuilder controls can match it.

// ue_bg_color event of a component
parent.backcolor = al_color
st_titre.backcolor = al_color

This is what lets you mix PBToolboxAI components and native PowerBuilder controls with no visible seam in dark mode.


4.6 Images and icons #

Everywhere a component expects an image path (button icon, tile, [picture=…]…), four forms are accepted:

FormExampleUsed for
Fileimg\logo.pngThe image as-is (png, jpg, gif, bmp, ico, svg, webp)
DLL resourceimg\packimages.dll:RIBBONAn image packaged in a resource DLL
mono:mono:img\save.svgFlat fill in the theme color: only the shape matters
tint:tint:img\logo_couleur.pngDuotone: the internal shading modulates the theme color

The path.dll:name form loads a resource from an image DLL (packimages.dll style), opened read-only (LOAD_LIBRARY_AS_DATAFILE, no code executed). This saves you from shipping hundreds of loose files.

Instant display: of_icon #

A small glyph passed through of_icon() is embedded in the command (no loading round trip): it shows up on the very first render, without the flicker of an icon loaded afterwards.

lnv_bar.of_add_button("save", "Save", uo_toolbar.of_icon("mono:img\save.svg"))

Transparent in practice: beyond a certain size, of_icon returns the original path (the image is then loaded and cached as usual).


4.7 Rich text markup #

Any label of any component accepts BBCode-style markup: tab title, button caption, status bar text, toast message, panel title, tooltip text…

The entries of the built-in menus follow the same rule — a tab's context menu, the ··· list of the tabs that no longer fit, a grid's column menus: the label shown by the menu is the one on the control, markup included.

The text is rendered as text nodes and <span> elements: no HTML injection is possible.

TagEffect
[b] [i] [u] [s] / [strike]Bold, italic, underline, strikethrough
[sub] [super]Subscript, superscript
[red]…[/red] (named colors)Text color (red, green, blue, orange, teal…)
[accent]…[/accent]Accent color of the current theme
[color=#rrggbb] / [color=accent]Text color
[bk=#rrggbb] / [backcolor=accent]Background color
[font=Consolas]Font
[size=14]Absolute size, in points (6 to 200)
[size+=30] / [size-=20]Relative size in % (20% by default)
[picture=path] / [picture=path,width,height]Inline image
[br] / [linebreak] / [br:3]Line break (or n breaks)
[separator]Horizontal rule
[hyperlink=url]…[/hyperlink]Clickable area: the link always opens in the user's browser, in every component. The ue_hyperlink(as_url) event is raised as well, for the components that expose it
[action=id]…[/action]Clickable area → ue_action(as_id) event, styled as a link
[invisibleaction=id]…[/invisibleaction]Clickable area → ue_action, without the link styling
[bullet]…[/bullet]Bullet: list item whose wrapped lines align on the first one instead of running back under the marker (hanging indent). [bullet=-] changes the marker
[foldarea:Title]…[/foldarea]Collapsible block: a clickable header ( / +) above an indented body. The title accepts markup
[foldarea-closed:Title]…[/foldarea]The same block, collapsed when displayed
[[ / ]]Escaping: [[b]] displays [b] without interpreting it
uo_texte.is_text = "Welcome to [b][accent]PBToolboxAI[/accent][/b] [size-=20]v1.0[/size-=20]" &
                   + "[br]See the [hyperlink=https://pbtoolboxai.net]documentation[/hyperlink]."

uo_tab.of_add_page("clients", "[b]Customers[/b] [size-=20](128)[/size-=20]", uo_clients)

uo_st.is_text = "The [[b]] tag makes text [b]bold[/b]"   // displays: The [b] tag makes text bold

Displaying data as-is. A value coming from your database may contain brackets: it would be interpreted. of_escape_markup(), available on every component, doubles them for you — wrap the data, never the markup you wrote yourself.

// Business data may contain brackets : without escaping it is INTERPRETED
// and the bracketed text vanishes.
ls_libelle = "Balance [net]"
uo_st.is_text = "Account : " + ls_libelle                          // shows : Account :
uo_st.is_text = "Account : " + uo_st.of_escape_markup(ls_libelle)  // shows : Account : Balance [net]

Text without any tag carries no overhead at all (fast path). Unknown tags are ignored and their content is preserved. A [hyperlink] opens everywhere — a label, a tab title, a status bar panel, a toast, a dialog: the core takes care of it. The ue_action event, on the other hand, is only emitted by interactive text components (statictext); elsewhere, [action] is formatting only.

Only http, https and mailto are opened. A label often carries data coming from your database: handing an arbitrary scheme to the system would turn a label into a program launcher.

A [foldarea] is a block: it takes the full width and folds on a click on its header, with no round trip to PowerBuilder. Blocks nest, and when the component follows the height of its content (ib_auto_height), that height is reported again on every fold. The title is markup too: nothing is bolded for you, [foldarea:[b]Total[/b]] does it.


← Shared foundation · Contents · Language and RTL →