PBToolboxAI v4 ← Site

tilesbox — u_pbt_tilesbox #

← Component reference · Guide contents

Panel of grouped tiles in home-screen style: four tile sizes, colors, badges, live tiles, collapsible groups and mouse rearranging.

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


At a glance #

Userobjectu_pbt_tilesbox
Item classesn_pbt_tilesbox_group (group) → n_pbt_tilesbox_tile (tile)
Used forOffering a home screen or a feature launcher: clickable tiles, readable from a distance, arranged by topic
Opt-in optionsib_reorderable: moving and resizing the tiles with the mouse

Quick start #

// window open event

// A tile is added at its ADDRESS : the group, then its own id
uo_tiles.of_add_group(/*key*/ "office", /*title*/ "Management")

// Then its tiles, each with its sizeuo_tiles.of_add_tile(/*keys*/ "office/clients",   /*label*/ "Customers",   /*icon_file*/ "mono:img\clients.svg",   /*size*/ "medium")
uo_tiles.of_add_tile(/*keys*/ "office/invoices",  /*text*/ "Invoices",  /*image*/ "mono:img\factures.svg",  /*size*/ "medium")
uo_tiles.of_add_tile(/*keys*/ "office/stock",     /*text*/ "Stock",     /*image*/ "mono:img\stock.svg",     /*size*/ "small")
// ue_tile_clicked event of uo_tiles : (string as_keys)
choose case as_keys
    case "office/clients"  ; open(w_clients)
    case "office/invoices" ; open(w_invoices)
    case "office/stock"    ; open(w_stock)
end choose

The model: groups and tiles #

The panel contains groups, each with its title and its collapsible header. Tiles belong to a group: their identifier is only unique within that group. Access therefore always goes through the full path. Additions live on the component: a handle carries properties and nothing else.

// Mandatory path : group then tile
uo_tiles.of_tile(/*keys*/ "office/invoices").is_badge = "12"

Events carry the full address as well (as_keys, the group then the tile). See Shared foundation · Items.


Properties #

PropertyTypeDefaultPurpose
ib_reorderablebooleanfalseLets users rearrange the tiles: with the mouse, or from the keyboard with Ctrl+Left/Right (one place in the group, in reading order) and Ctrl+Up/Down (to the previous or next group); a right click resizes. Mouse and keyboard raise the same ue_tile_moved
is_scrollstringSCROLL_HORIZONTALScrolling direction: SCROLL_VERTICAL (tiles wrap onto the next line) or SCROLL_HORIZONTAL (columns, home-screen style)
ib_show_group_headersbooleantruefalse hides the group headers. Handy when the panel holds only one group: there is nothing to name and the header just eats up height. Without a header there is no chevron either: the groups always show expanded
ii_anim_durationinteger180Duration in milliseconds of the collapse / expand animation of a group; 0 = instant
ii_tile_unitinteger0Base size of a tile, in pixels: every tile size (small, medium, wide, large) is a multiple of it, so one value makes the whole board denser or airier. 0 gives it back to the theme
ii_badge_sizeinteger0Tile badge height in pixels, for the whole box (0 = the size that comes from the theme). The font size follows on its own. A badge color belongs to the tile because it carries a meaning; its size does not
is_theme_stylestring""Visual style of the component (THEME_STYLE_* constants); empty = the application's, followed at every change
is_theme_modestring""Light or dark variant (THEME_MODE_* constants); empty = the application's, followed at every change
il_theme_accentlong-1Accent colour of this component (-1 = the application accent, or the theme's)
is_tooltipstring""Simple tooltip shown when hovering the component
is_super_tooltip_titlestring""Title of the rich tooltip (takes precedence over is_tooltip)
is_super_tooltip_textstring""Text of the rich tooltip (rich markup accepted)
is_super_tooltip_imagestring""Image of the rich tooltip

Methods #

MethodPurpose
of_group (string as_key) → n_pbt_tilesbox_groupReturns the handle of a group (created on first access). On a group that does not exist it writes nothing — it never creates the group, of_add_group does; a key that cannot name a group (empty, with / or ``) gives an inert handle that reads empty
of_add_group (string as_key, string as_title) → longAdds a group. Returns 0 once applied, -5 when the key is empty, holds a / or a `, or is already taken, -2` when the component is not created
of_add_tile (string as_keys, string as_text, string as_image, string as_size) → longAdds a tile at its address, "office/clients": the group it lands in, then its own id. Returns 0 once applied, -5 when the address is not group/tile, when the group was never added with of_add_group, when the key is already taken in that group (or empty, or holds a `), -2` when the component is not created. An overload drops the icon and the size
of_tile (string as_keys) → n_pbt_tilesbox_tileReturns the handle of one tile, by its address (created on first access). An address that is not group/tile gives an inert handle: it writes nothing and reads empty
of_add_live_item (string as_keys, string as_text) → longAdds a face to the live band of a tile, at its address; from two faces on, the tile cycles between them. The first face clears is_live_text; of_tile(address).of_count() reads how many faces it holds. Returns 0 once applied, -5 on an empty text or when no tile lives at that address, -2 when the component is not created
of_clear_live_items (string as_keys) → longClears the live band of one tile: it stops cycling and goes back to its plain face. Returns 0 once applied, -5 when no tile lives at that address, -2 when the component is not created
of_remove_group (string as_key)Removes a group and all its tiles. Returns 0 once applied, -5 when there is no such group, -2 when the component is not created
of_insert_tile (string as_keys, string as_text, integer ai_index)Inserts a tile at a precise position within its group (position counted from 1). Returns 0 once applied, -5 for the same refusals as of_add_tile, -2 when the component is not created
of_insert_tile (string as_keys, string as_text, string as_image, string as_size, integer ai_index)Same, with the icon and the size (small / medium / wide / large): a tile is a picture with a label under it, and its size shows in the grid. Returns 0 once applied, -5 for the same refusals as of_add_tile, -2 when the component is not created
of_move_tile (string as_keys, integer ai_index)Moves an existing tile to another position within its group, keeping its state. Returns 0 once applied, -5 when no tile lives at that address, -2 when the component is not created
of_remove_tile (string as_keys)Removes a single tile, the rest of the panel is preserved. Returns 0 once applied, -5 when no tile lives at that address, -2 when the component is not created
of_clear ( )Empties the panel (groups and tiles); the handles handed out for them are released. Returns 0 once applied, -2 when the component is not created
of_get_layout ( )Reads the current layout back as JSON: the groups in their order, folded or not, visible or not, and their tiles in order with their size and visibility. An opaque text: store it as is, then hand it back to of_set_layout
of_set_layout (string as_state_json)Restores a layout read with of_get_layout or received with ue_layout_changed: order, folded groups, sizes and visibility. A tile is found back by its group and its key; whatever the layout does not name keeps its place. Raises no event. Returns 0 once applied, -5 on an empty text or one that is not JSON, -2 when the component is not created
of_reset ( )Clears the content and resets every property to its default. Returns 0 once applied, -2 when the component is not created

On a group — n_pbt_tilesbox_group #

MemberTypeDefaultPurpose
is_titlestringthe keyTitle shown in the header, rich text markup accepted
ib_collapsedbooleanfalseCollapsed group: its tiles are hidden under the header. Setting it raises ue_group_toggled when the state changes, like a click on the header
ib_enabledbooleantrueWhole group grayed out and inert: its header no longer folds it, its tiles no longer answer, no tile can be dropped into it
ib_visiblebooleantrueGroup hidden, without being removed from the model
il_back_color · il_text_color · il_back_color_hover · il_text_color_hover · il_accentlong-1Colors of the group header — background, title, both under the pointer, and an accent rule under the header. They do not pass down to the tiles, which keep their own

On a tile — n_pbt_tilesbox_tile #

MemberTypeDefaultPurpose
is_titlestring""Text shown on the tile. Set by of_add_tile; writing here changes it, reading tells what the tile shows right now
is_imagestring""Icon of the tile (disk path or resources.dll:name). An empty string takes the icon away
is_sizestringSIZE_MEDIUMSize: SIZE_SMALL, SIZE_MEDIUM, SIZE_WIDE, SIZE_LARGE
il_back_color · il_text_colorlong-1Background and text of this tile, as RGB(r,g,b) (-1 = whatever the theme gives)
il_back_color_hover · il_text_color_hoverlong-1The same two, but under the pointer
is_badgestring""Badge shown in the corner of the tile (a counter, a short status)
il_badge_colorlong-1Badge background, as a PowerBuilder RGB value (-1 = the color that comes from the theme; 0 is black)
is_live_textstring""Fixed text overlaid on the tile; the first face of of_add_live_item clears it (it then reads empty), rich text markup accepted
ib_enabledbooleantrueTile grayed out and not clickable
ib_visiblebooleantrueTile hidden, without being removed from its group

Icons accept every form recognized by the library: path, mono:, tint:, DLL resource. On a large tile, prefer a crisp high-resolution image or an SVG.


The four sizes #

SizeFootprintTypical use
small1 cellSecondary shortcut; the caption is not shown, the tooltip takes over
medium2 × 2 cellsReference size: icon and caption
wide4 × 2 cellsElongated tile, ideal for live text
large4 × 4 cellsFlagship tile of the screen

Events #

EventRaised when
ue_tile_clicked (string as_keys)A tile is clicked
ue_tile_moved (string as_keys, string as_to_key, long al_index)The user moved a tile, with the mouse or with Ctrl+arrows (of_move_tile raises nothing): as_keys is the address it came from, as_to_key the destination group, al_index the new position
ue_group_toggled (string as_key, boolean ab_collapsed)A group is collapsed or expanded: from its header, or by its ib_collapsed set by your code (nothing when it already is in that state)
ue_tile_context (string as_keys)Right-click on a tile
ue_tile_resized (string as_keys, string as_size)The user gave a tile another size from its right-click menu (shown only while ib_reorderable is true); as_size carries the new size
ue_layout_changed (string as_layout_json)The user changed the arrangement: a tile moved, a tile resized from its menu, a group folded or opened from its header. Carries the whole layout, the same text of_get_layout returns. What the application changes by code does not raise it (a group's ib_collapsed raises ue_group_toggled only)
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 #

Mixed sizes within a single group #

// Start from an empty box, with one group
uo_tiles.of_clear()
uo_tiles.of_add_group(/*key*/ "home", /*title*/ "Home")

// Four tiles, in four sizes
uo_tiles.of_add_tile(/*keys*/ "home/dashboard", /*text*/ "Dashboard", /*image*/ "mono:img\chart.svg",  /*size*/ "large")
uo_tiles.of_add_tile(/*keys*/ "home/clients", /*text*/ "Customers",         /*image*/ "mono:img\users.svg",  /*size*/ "medium")
uo_tiles.of_add_tile(/*keys*/ "home/agenda",  /*text*/ "Calendar",          /*image*/ "mono:img\cal.svg",    /*size*/ "wide")
uo_tiles.of_add_tile(/*keys*/ "home/help",    /*text*/ "Help",            /*image*/ "mono:img\help.svg",   /*size*/ "small")

Badge, color and live tile #

// Local variables
n_pbt_tilesbox_tile lnv_tile

// A badge signals a number of pending items
uo_tiles.of_tile(/*keys*/ "home/clients").is_badge = String(ll_new_count)

// Red when the backlog becomes critical, theme color otherwise (-1)
if ll_new_count > 50 then
    uo_tiles.of_tile(/*keys*/ "home/clients").il_badge_color = RGB(/*red*/ 200, /*green*/ 30, /*blue*/ 30)
else
    uo_tiles.of_tile(/*keys*/ "home/clients").il_badge_color = -1
end if

// A dedicated color brings a tile forward : background, text, and what each
// becomes under the pointer
lnv_tile = uo_tiles.of_tile(/*keys*/ "home/dashboard")
lnv_tile.il_back_color = RGB(/*red*/ 0, /*green*/ 103, /*blue*/ 192)
lnv_tile.il_back_color_hover = RGB(/*red*/ 0, /*green*/ 80, /*blue*/ 150)
lnv_tile.il_text_color = RGB(/*red*/ 255, /*green*/ 255, /*blue*/ 255)

// LIVE tile : each face added cycles in turn on the tile. A face is added
// from the COMPONENT, at the address of the tile
uo_tiles.of_clear_live_items(/*keys*/ "home/agenda")
uo_tiles.of_add_live_item(/*keys*/ "home/agenda", /*text*/ "3 appointments today")
uo_tiles.of_add_live_item(/*keys*/ "home/agenda", /*text*/ "Team meeting at 2:00 PM")
uo_tiles.of_add_live_item(/*keys*/ "home/agenda", /*text*/ "2 overdue follow-ups")

Fixed text rather than a cycling banner #

// is_live_text puts ONE permanent text on the tile (rich text markup accepted)
uo_tiles.of_tile(/*keys*/ "home/dashboard").is_live_text = "[b]48[/b] orders today"

Collapsible groups #

// Local variables
n_pbt_tilesbox_group lnv_admin

// A group and its two tiles, then a handle on the group
uo_tiles.of_add_group(/*key*/ "admin", /*title*/ "Administration")
uo_tiles.of_add_tile(/*keys*/ "admin/users",  /*text*/ "Users", /*image*/ "mono:img\users.svg", /*size*/ "medium")
uo_tiles.of_add_tile(/*keys*/ "admin/params", /*text*/ "Settings", /*image*/ "mono:img\gear.svg",  /*size*/ "medium")
lnv_admin = uo_tiles.of_group(/*key*/ "admin")

// This group starts collapsed : users open it if they need it
lnv_admin.ib_collapsed = true

// A whole group can also be grayed out according to permissions
lnv_admin.ib_enabled = (is_profile = "ADMIN")
// ue_group_toggled event of uo_tiles
if as_key = "admin" and not ab_collapsed then of_trace("Administration pane opened")

A single-group panel, without a header #

// A single group has nothing to name : its header just eats up height.
// Without a header there is no chevron either : the tiles always stay visible.
uo_tiles.ib_show_group_headers = false
uo_tiles.of_add_group(/*key*/ "all", /*title*/ "")

Moving a tile between two groups #

With ib_reorderable = true, users drag a tile wherever they want, including from one group to another. The ue_tile_moved event delivers both groups and the new position. From the keyboard, the focused tile moves one place forward or back with Ctrl+Left/Right (in reading order: reversed right to left) and to the previous or next group with Ctrl+Up/Down — same ue_tile_moved, same refusals (key already taken, group disabled or folded). The Tab key stops only once per group: the arrows walk the tiles.

// Let users rearrange the tiles : false by default, ask for it
uo_tiles.ib_reorderable = true
// ue_tile_moved event of uo_tiles
// as_keys = where it comes from, as_to_key = where it goes, al_index = position
n_pbt_utils lnv_utils   // autoinstantiate : nothing to create, nothing to destroy
if lnv_utils.of_parent(/*keys*/ as_keys) <> as_to_key then
    of_trace("Tile " + lnv_utils.of_leaf(/*keys*/ as_keys) + " moved from " + lnv_utils.of_parent(/*keys*/ as_keys) + " to " + as_to_key)
end if
// ue_layout_changed event of uo_tiles : store the chosen layout
is_layout_json = as_layout_json
// On the next opening : restore the screen the way the user had arranged it
if is_layout_json <> "" then uo_tiles.of_set_layout(/*state_json*/ is_layout_json)

To freeze the screen, simply turn rearranging off:

// Freeze the screen : the tiles stay where they are
uo_tiles.ib_reorderable = false

Rearranging from code #

// Put the "help" tile at the head of its group (positions counted from 1)
uo_tiles.of_move_tile(/*keys*/ "home/help", /*index*/ 1)

// Insert a new tile in second position
uo_tiles.of_insert_tile(/*keys*/ "home/quotes", /*text*/ "Quotes", /*index*/ 2)

// Remove a tile without touching the rest
uo_tiles.of_remove_tile(/*keys*/ "home/help")

// Each call returns -5 when the address names nothing (or, on insertion, a key already taken)

Reacting to a right-click #

// ue_tile_context event of uo_tiles : (as_keys)
// Remember the targeted tile, then open your own context menu
n_pbt_utils lnv_utils   // autoinstantiate : nothing to create, nothing to destroy

// One address : split it if you need the two halves.
is_target_group = lnv_utils.of_parent(/*keys*/ as_keys)
is_target_tile = lnv_utils.of_leaf(/*keys*/ as_keys)
of_open_tile_menu(PointerX(), PointerY())

Best practices #

Inherited from the common base #

These members exist on every visual component — they are not specific to this one. They are detailed once, in the transverse chapters; this table only says where to read them.

MembersRoleDetailed in
of_count · of_keys_at · of_hasWalk what the component holds3.2 Items
of_resetPut the component back to zero3.6 Resetting a component: of_reset()
of_register_shortcut · of_clear_shortcutsThe component's keyboard chords3.5 Keyboard shortcuts
of_is_created · of_is_ready · of_get_last_errorWhether it was born, whether it is ready, what failed3.7 Diagnostics
of_save_as_png · of_save_as_jpgExport the rendering as an image3.8 Exporting the rendering as an image
of_set_redrawGroup changes into a single repaint3.10 Best practices
of_preload_iconsIcons shown with no delayInstant display: of_icon
of_set_translationTranslate one of the component's labels5.2 Adapting a label: of_set_translation
of_focus_webviewGive the component the focus6.4 Keyboard and focus
of_print · of_print_to_pdfPrint, or write a PDF6.9 Printing
of_set_property · of_get_property · of_component_nameDriving a property by its name3.1 The property engine

Two helpers are not inherited: of_icon and of_escape_markup live on n_pbt_utils. Declare one — n_pbt_utils lnv_utils, nothing to create — and call them on it.

← Component reference · Guide contents