PBToolboxAI v4 ← Site

breadcrumb — u_pbt_breadcrumb #

← Component reference · Guide contents

Breadcrumb trail: the clickable path that tells the user where they are, and takes them back up to any level above in one click.

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


At a glance #

Userobjectu_pbt_breadcrumb
Item classn_pbt_breadcrumb_item (of_item(address)) · n_pbt_breadcrumb_child (of_child(address))
Used forSaying where you are in a hierarchy, and letting you climb back out of it
PrincipleYou describe the path; the folding, the menu and the layout are ours

Quick start #

// Every time the user goes down a level
uo_crumbs.of_add_item(/*keys*/ "home",                /*text*/ "H")
uo_crumbs.of_add_item(/*keys*/ "home/clients",        /*text*/ "C")
uo_crumbs.of_add_item(/*keys*/ "home/clients/dupont", /*text*/ "D")

// The last one added becomes the current location, and it is clickable too

A segment is named by its address: the keys from the root, joined by / — "home/clients/dupont". A bare key is no longer enough as soon as it repeats at two levels of the same trail: the component then refuses to guess, rather than take you somewhere you never asked for.

That is exactly what ue_item_clicked hands you, and exactly what of_truncate, of_item or of_add_child take back: what you receive goes straight back in.


A click reports, it does not cut #

Clicking a segment does not shorten the trail. Going back up means leaving a screen, and leaving a screen often means saving first — which no click can decide. The component tells you what was clicked; you are the one who cuts, through of_truncate, once your checks have passed.

This is the same division of labour as the stepbar, and for the same reason. A component that moves by itself forces the application to undo a move already made, instead of simply choosing whether it happens.

Two segments never report anything: a disabled segment, a hidden one. The last one — where you are — answers like the others, until ib_last_clickable says no.

// event ue_item_clicked : (string as_keys)
// Your checks first - leaving the screen is your decision, not a click's
if not of_can_leave() then return
uo_crumbs.of_truncate(/*keys*/ as_keys)
of_open_screen(as_keys)

When the path is too long #

A path is as long as the data makes it, and the width is what it is. is_overflow_mode says what gives.

ConstantWhat happens
OVERFLOW_COLLAPSEThe middle folds into a … that opens what it hides — the default
OVERFLOW_SCROLLLabels stay whole, the strip slides (the mouse wheel slides it too)
OVERFLOW_SHRINKThe middle segments give ground, down to a letter and an ellipsis; the first one and where you are give ground last. With room to spare, nothing is cut

Neither the first segment nor the last ever folds. Losing the root loses the anchor everyone goes back to; losing the end loses where you are.

A folded segment reports exactly like the others: picking it from the … raises the same ue_item_clicked. Being hidden by the width does not change what a segment means.

ii_max_visible sets a hard ceiling, whatever the room. Leave it at 0 — the default — to let the width decide, which is what a breadcrumb should normally follow.


The sibling menu #

of_add_child gives a segment its own drop-down: the other branches of that level. That is what saves a walk back to the root just to go down a neighbouring folder.

The chevron after the segment then becomes the button that opens them — the very separator, as in the Windows explorer: one chevron, one meaning to learn. Picking a branch raises ue_child_clicked, and here too the trail does not move by itself. On the keyboard, Down arrow on a segment opens its branches, and on the … what it hides.

// Two branches under Clients : its chevron lists them
uo_crumbs.of_add_child(/*keys*/ "home/clients/durand", /*text*/ "D")
uo_crumbs.of_add_child(/*keys*/ "home/clients/martin", /*text*/ "M")

Branches on demand #

Posing every branch up front does not hold on a deep tree, nor on a database. The Windows explorer reads a folder only when its chevron opens; the trail does the same: flag a segment with ib_has_children, its chevron shows at once, and opening it raises ue_children_needed. You pose the branches in that event, and the menu opens once it returns, with what the segment holds at that moment. A big folder is no problem: adding 30 000 branches reads nothing back, and in the open menu typing the first letters ("Win") jumps to the first branch that starts that way, as in the explorer.

// The segment promises : the chevron shows, nothing is read
uo_crumbs.of_item(/*keys*/ "home/clients").ib_has_children = true

// In ue_children_needed(as_keys) : read now, then the menu opens
uo_crumbs.of_clear_children(/*keys*/ as_keys)
uo_crumbs.of_add_child(/*keys*/ as_keys + "/durand", /*text*/ "Durand SARL")

Typing the path #

With ib_editable, the empty part of the bar behaves like the address bar of the Windows explorer: a click (or F2, or of_edit) turns the trail into a text field holding the displayed address — the keys joined by /, what of_path returns. Enter raises ue_path_entered with the text as typed; Escape cancels. The trail does not move by itself, for the same reason a click does not shorten it: only your application knows what the words mean. ii_edit_skip leaves the first segments out of the field — the root that names the machine — and puts them back in front of what was typed when it is reported.


Properties #

PropertyTypeDefaultRole
is_separatorstringchevronThe glyph between segments (SEPARATOR_* constants). It turns round on its own in a right-to-left language: pick a meaning, not a direction. A separator that opens branches keeps the chosen glyph: the hover and the pointer are what say it opens
is_overflow_modestringcollapseWhat gives when the path no longer fits (OVERFLOW_* constants). Under scroll, the strip follows where you are
ii_max_visibleinteger0A hard ceiling on the number of segments shown, the … not counted. 0 leaves it to the width
ib_last_clickablebooleantrueIs the last segment — where you are — clickable? True by default: a trail is also how one asks to reload what is on screen, and what the click does is your application's business. Set it false when your trail only ever navigates
ib_editablebooleanfalseCan the path be typed? True: a click on the empty part of the bar (or F2, or of_edit) turns the trail into a text field holding the address of where you are (what of_path gives); Enter raises ue_path_entered, and so does leaving the field once it was changed; Escape cancels. A field left unchanged says nothing; clicking another control of the application validates the text, switching to another application keeps the typing. The trail never moves by itself
ib_allow_dropbooleanfalseOpt-in: accepts files dropped from Windows Explorer on a segment. The segment under the cursor lights up during the drag, and ue_drop_files names it with the full paths
ii_edit_skipinteger0Number of leading segments left out of the text field — a root that names the machine is not something one types. They are put back in front of what was typed when it is reported: the address stays complete
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 #

MethodRole
of_add_item (string as_keys, string as_text)Appends a segment at the end: it becomes the current location. as_keys is a bare key or an address; an address is taken only when it lands where it says — its levels above the last one must be the address of the last segment. Returns 0 once applied, -5 on an empty key, a key holding ` or an address that would land elsewhere, -2` when the component is not created
of_add_item (string as_keys, string as_text, string as_image)Same, with the icon shown before the label — third argument, as everywhere else in the library. An empty label gives an icon-only segment (the root's house). Returns 0 once applied, -5 on an empty key, a key holding ` or an address that would land elsewhere, -2` when the component is not created
of_insert_item (string as_keys, string as_text, integer ai_index)Inserts at the position you choose (first position = 1). An overload takes the icon as well. An address is taken only when its levels above the last one are the address of the segment it follows. The segments after it move one level down: their addresses change, their handles are released, their colours and tooltips follow them. Returns 0 once applied, -5 on an empty key, a key holding ` or an address that would land elsewhere, -2` when the component is not created
of_remove_item (string as_keys)Removes one segment, by its address; the others keep their state. The segments after it move up one level: the handles of the removed segment and of everything after it are released. Returns 0 once applied, -5 when the address designates no segment (unknown, or a bare key that repeats), -2 when the component is not created
of_truncate (string as_keys)Drops everything after that segment, which becomes the current location. This is the move a breadcrumb exists for; an unknown address or a bare key that repeats changes nothing and returns -5. The handles of the segments dropped are released. Returns 0 once applied, -5 when the address designates no segment, -2 when the component is not created
of_clear ( )Empties the trail; the handles given out for its segments and branches are released. Returns 0 once applied, -2 when the component is not created
of_add_child (string as_keys, string as_text)Adds a sibling branch at the given address: the segment above it grows a chevron that opens them. An overload takes the icon as well. Returns 0 once applied, -5 when the segment above does not exist, when the branch key is already taken under it or holds `, -2` when the component is not created
of_add_children (string as_keys, string as_child_keys[], string as_texts[])Adds a whole level of sibling branches under the segment as_keys, in one call: each text of as_texts (and each image, in the overload that also takes a list of images) goes with the key at the same rank in as_child_keys; a missing text shows the key. The same branches as a loop of of_add_child, without one round trip each: a folder of 30,000 sub-folders opens its menu at once. Returns 0 once applied (an empty list adds nothing), -5 when the address designates no segment, or when a key is empty, holds / or `, is already taken under the segment or comes twice in the list — nothing is added then, -2` when the component is not created
of_clear_children (string as_keys)Removes the sibling branches of a segment, and their handles; its separator becomes a plain mark again. Returns 0 once applied, -5 when the address designates no segment, -2 when the component is not created
of_remove_child (string as_keys)Removes one sibling branch, by its own address, and its handle; the last one gone, the chevron goes back to a plain separator. Returns 0 once applied, -5 when the segment or the branch does not exist, -2 when the component is not created
of_child (string as_keys)The handle to a sibling branch — the address of_add_child took — to rename it, grey it or hide it. The menu is a native popup: it draws a text, an image, greyed and hidden, nothing more — the tooltip and the colours a handle inherits are ignored there. An address with one level has no segment above it: its handle is inert
of_path ( )Where you are, as an address: the keys of every segment up to the last visible one, separated by /. A hidden segment stays in it — it is part of the address — and the demo version's limit never cuts it: of_truncate(of_path()) always lands. Read live: an application that rebuilt this string by hand would end up disagreeing with the trail
of_edit ( )Opens the text field of the path — the same one a click on the empty part of the bar opens — from a menu entry or a button of your own. Needs ib_editable. Returns 0 once applied, -4 when ib_editable is false (nothing opens), -2 when the component is not created
of_item (string as_keys)The handle of one segment, to rename, grey out or hide it later. It lives as long as its segment: of_clear, of_remove_item and of_truncate release the handles of the segments they take away
of_set_redraw (boolean)Groups a burst of changes into a single render. Returns 0 once applied, -2 when the component is not created
of_save_as_png (string) · of_save_as_jpg (string)Exports the rendering as an image. Returns 0 once the image is written, -4 when writing fails, -2 when the component is not created

Events #

EventRaised when
ue_item_clicked (string as_keys)A segment was clicked — in the trail, or in the … that hides it. The trail does not shorten itself: call of_truncate once your checks have passed
ue_item_rclicked (string as_keys)Right-click on a segment — a context menu of your own, usually. as_keys is its full address, as in ue_item_clicked
ue_child_clicked (string as_keys)A sibling branch was picked in a segment's menu; as_keys is the address of the branch, ready to go straight back into of_add_item
ue_children_needed (string as_keys)The chevron of a segment flagged ib_has_children opens: pose its branches now (of_add_child), the menu opens once the event returns, with what the segment holds at that moment. Asked at every opening: clear and refill when the branches may have changed, do nothing when what is there still holds
ue_path_entered (string as_path)The user typed a path in the bar (ib_editable) and pressed Enter — or left the field after changing it; as_path is the text as typed, with the ii_edit_skip leading segments put back in front. Clicking another control of the application counts as leaving the field. Escape, an unchanged field or another application brought to the front report nothing. The trail does not move by itself: check the words, then rebuild it with of_clear and of_add_item if you agree
ue_drop_files (string as_keys, string as_files[])Files were dropped from the Explorer on a segment (ib_allow_drop): as_keys is the address of the segment under the cursor, empty when the drop landed beside the trail; as_files the full paths
ue_drag_enter ( ) · ue_drag_leave ( )A file drag from the Explorer entered the component, or left it without dropping — a drop raises ue_drop_files alone
ue_auto_height (long al_height)The bar announces the height it needs — one row, decided by the font and the theme; the userobject is already resized, move whatever sits below it
ue_ready ( )The component has finished loading; everything sent before was replayed
ue_runtime_missing ( )The WebView2 runtime is missing: the component stays empty
ue_bg_color (long al_color)The component worked out its theme background; the userobject has already adopted it (backcolor)

The trail does not navigate. It says where you are and reports what it is asked; your application is what opens the screen — the same action, triggered from a menu or from the trail, therefore goes through the same code.


Item properties #

PropertyTypeDefaultRole
is_textstring""The segment's label, changeable without rebuilding the trail (rich markup accepted: a label that comes from DATA — a folder name — goes through of_escape_markup of n_pbt_utils first, or [b]Drafts would show in bold without its brackets)
is_imagestring""The icon shown before the label (mono: and tint: prefixes accepted)
ib_enabledbooleantrueA disabled segment is greyed and reports nothing: the level exists in the path, but it cannot be gone back to (rights, a record being edited). Its chevron opens nothing either, and a file drag does not light it up
ib_visiblebooleantrueA hidden segment leaves the trail, separator included — useful for a technical level the user has no business seeing. It is kept: showing it again needs no rebuild
ib_has_childrenbooleanfalseFlagged: there is something under this segment. Its chevron shows with nothing behind it yet, and opening it raises ue_children_needed, where the branches are read at that moment. A segment whose branches were posed with of_add_child needs no flag

Child properties #

Obtained through of_child(address). The menu is a native popup: a property changed while it is open shows at the next opening.

PropertyTypeDefaultRole
is_textstring""The label of the branch in the menu
is_imagestring""The icon shown before the label
ib_enabledbooleantrueA greyed branch stays in the menu and cannot be chosen — no rights on that branch
ib_visiblebooleantrueA hidden branch leaves the menu without being removed; the last one hidden closes the chevron

Examples #

Following it as the user navigates #

// Rebuild the trail in a single redraw : freeze, clear, add, insert, redraw
uo_crumbs.of_set_redraw(/*on*/ false)
uo_crumbs.of_clear()
uo_crumbs.of_add_item(/*keys*/ "home", /*text*/ "H", /*image*/ "mono:img\packimages.dll:svg/samples/folder-open")
uo_crumbs.of_insert_item(/*keys*/ "home/region", /*text*/ "R", /*index*/ 2)
uo_crumbs.of_set_redraw(/*on*/ true)

Going back up on a click #

// Cut the trail after Clients, then read the path that remains
uo_crumbs.of_truncate(/*keys*/ "home/clients")
ls_path = uo_crumbs.of_path()

A forbidden level, a hidden one #

// The level exists, but it cannot be gone back to
uo_crumbs.of_item(/*keys*/ "home/clients/orders").ib_enabled = false

// And this one is none of the user's business : out of the trail, separator included
uo_crumbs.of_item(/*keys*/ "home").ib_visible = false
// Slash separator, scrolling on overflow, at most 4 segments shown, last one not clickable
uo_crumbs.is_separator = uo_crumbs.SEPARATOR_SLASH
uo_crumbs.is_overflow_mode = uo_crumbs.OVERFLOW_SCROLL
uo_crumbs.ii_max_visible = 4
uo_crumbs.ib_last_clickable = false

// Remove one segment, then the branches offered under another
uo_crumbs.of_remove_item(/*keys*/ "home/region")
uo_crumbs.of_clear_children(/*keys*/ "home/clients")

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