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 #
| Userobject | u_pbt_breadcrumb |
| Item class | n_pbt_breadcrumb_item (of_item(address)) · n_pbt_breadcrumb_child (of_child(address)) |
| Used for | Saying where you are in a hierarchy, and letting you climb back out of it |
| Principle | You 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.
| Constant | What happens |
|---|---|
OVERFLOW_COLLAPSE | The middle folds into a … that opens what it hides — the default |
OVERFLOW_SCROLL | Labels stay whole, the strip slides (the mouse wheel slides it too) |
OVERFLOW_SHRINK | The 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 sameue_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 #
| Property | Type | Default | Role |
|---|---|---|---|
is_separator | string | chevron | The 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_mode | string | collapse | What gives when the path no longer fits (OVERFLOW_* constants). Under scroll, the strip follows where you are |
ii_max_visible | integer | 0 | A hard ceiling on the number of segments shown, the … not counted. 0 leaves it to the width |
ib_last_clickable | boolean | true | Is 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_editable | boolean | false | Can 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_drop | boolean | false | Opt-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_skip | integer | 0 | Number 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_style | string | "" | Visual style of the component (THEME_STYLE_* constants); empty = the application's, followed at every change |
is_theme_mode | string | "" | Light or dark variant (THEME_MODE_* constants); empty = the application's, followed at every change |
il_theme_accent | long | -1 | Accent colour of this component (-1 = the application accent, or the theme's) |
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 |
Methods #
| Method | Role | |
|---|---|---|
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 #
| Event | Raised 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 #
| Property | Type | Default | Role |
|---|---|---|---|
is_text | string | "" | 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_image | string | "" | The icon shown before the label (mono: and tint: prefixes accepted) |
ib_enabled | boolean | true | A 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_visible | boolean | true | A 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_children | boolean | false | Flagged: 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.
| Property | Type | Default | Role |
|---|---|---|---|
is_text | string | "" | The label of the branch in the menu |
is_image | string | "" | The icon shown before the label |
ib_enabled | boolean | true | A greyed branch stays in the menu and cannot be chosen — no rights on that branch |
ib_visible | boolean | true | A 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 #
- Give every segment the key of the screen it opens: your
ue_item_clickedbecomes achoose caseanyone can read, and the same code serves the menu. - Call
of_truncateinside your click handler, not before: that is what guarantees a screen is never left without your checks. - Leave
ii_max_visibleat0unless a design demands otherwise. A trail that follows the width always shows as much as fits. - Name segments with words the user recognises — the customer's name, not their id. A trail is read, not decoded.
- Only put levels in it that can really be returned to. A segment that fails every other time costs the whole trail its credibility; when a level is temporarily out of reach,
ib_enabledsays so without lying. - A breadcrumb says a place, not a progression: for "step 2 of 5", the stepbar is what you want.
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.
| Members | Role | Detailed in |
|---|---|---|
of_count · of_keys_at · of_has | Walk what the component holds | 3.2 Items |
of_reset | Put the component back to zero | 3.6 Resetting a component: of_reset() |
of_register_shortcut · of_clear_shortcuts | The component's keyboard chords | 3.5 Keyboard shortcuts |
of_is_created · of_is_ready · of_get_last_error | Whether it was born, whether it is ready, what failed | 3.7 Diagnostics |
of_save_as_png · of_save_as_jpg | Export the rendering as an image | 3.8 Exporting the rendering as an image |
of_set_redraw | Group changes into a single repaint | 3.10 Best practices |
of_preload_icons | Icons shown with no delay | Instant display: of_icon |
of_set_translation | Translate one of the component's labels | 5.2 Adapting a label: of_set_translation |
of_focus_webview | Give the component the focus | 6.4 Keyboard and focus |
of_print · of_print_to_pdf | Print, or write a PDF | 6.9 Printing |
of_set_property · of_get_property · of_component_name | Driving a property by its name | 3.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.