PBToolboxAI v5 ← Site

carousel — u_pbt_carousel #

← Component reference · Guide contents

carousel leafs through items, only one of which is in front: the Cover Flow of iTunes — the current item face on, its neighbours turned in perspective and overlapping, a reflection under them —, a flat strip where several items show in full, a stack of cards, or a full-surface banner that shows one item at a time. Each item is a picture, a rich text or a card (picture, title, subtitle, body written by a template, tags, badge). The user clicks a neighbour, swipes with inertia, turns the wheel, types the first letters of a title or uses the keyboard; ue_current_changed tells you which item is in front. The carousel is filled by hand or from a DataStore, and can move on by itself, with a PAUSE button.

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


At a glance #

Userobjectu_pbt_carousel
Item classn_pbt_carousel_item (of_item(key))
Used forLeafing through album covers, products, photos, records, news or onboarding steps
PrincipleAn item has a unique key; only one is in front at a time, and it is drawn as a picture, a text or a card
Opt-in optionsib_veto_changes, ib_loop, ib_reflection, il_autoplay_ms
Demo mode limitThe first 6 visible items drawn — see demo mode

Quick start #

// Three album covers : a key, a title, a picture
uo_covers.of_add_item(/*key*/ "c1", /*title*/ "Northern Lights", /*image*/ "img\packimages.dll:SVG/COVERS/COVER-01")
uo_covers.of_add_item(/*key*/ "c2", /*title*/ "Blue Hour", /*image*/ "img\packimages.dll:SVG/COVERS/COVER-02")
uo_covers.of_add_item(/*key*/ "c3", /*title*/ "Evergreen", /*image*/ "img\packimages.dll:SVG/COVERS/COVER-03")

// A reflection under the covers, as on a glossy floor
uo_covers.ib_reflection = true
// event ue_current_changed of uo_covers : (string as_from_key, string as_key)
// The album now in front, in the title of the window ("" : the carousel is empty)
if as_key <> "" then Parent.Title = this.of_item(/*key*/ as_key).is_title

Four layouts, three faces #

is_style chooses the layout: STYLE_COVERFLOW (the default) puts the current item face on and turns its neighbours in perspective, overlapping, spread over the width available and darker with the distance (ii_angle, ii_spacing, ib_shade); STYLE_CAROUSEL lines up a flat strip where several items show in full; STYLE_STACK piles up cards — the current one in front, the next ones behind it, the previous ones gone to the side; STYLE_HERO shows one item on the whole surface, the next one sliding or fading in (is_transition): the banner of a dashboard. A tile keeps a proportion (is_item_ratio; empty: 1:1 for pictures, 3:4 for cards, 16:9 for texts) and the content of a card or a text follows its size, between a readable floor and a ceiling. ib_reflection puts a reflection under the items, ii_item_width and ii_item_height set the size to the pixel.

Why faces rather than components inside the carousel? Every component of the library is a control of its own, with its own web page (a WebView2): it cannot sit inside the page of another one. So the carousel draws by itself what a picture, a statictext or a kanban card would show — these are the three faces of one item, and a thousand items cost one single control. is_item_style chooses the face of every item, is_style of an item chooses its own (empty: it follows the carousel). With ITEM_AUTO (the default), the face follows from what the item holds:

of_item(key).of_face() tells the face chosen. A picture is a file of the workstation, a resource file.dll:name, a mono: or tint: glyph drawn in the theme, or an https:// address; an http:// address is refused and raises ue_image_failed with REASON_INSECURE, a missing file with REASON_NOT_FOUND.

// The covers as a pile of cards, the whole picture in each tile
uo_covers.is_style = u_pbt_carousel.STYLE_STACK
uo_covers.is_fit = u_pbt_carousel.FIT_CONTAIN

// This item is a card, whatever it holds
uo_covers.of_item(/*key*/ "c2").is_style = u_pbt_carousel.ITEM_CARD

A row aligned at its start #

On a flat strip (STYLE_CAROUSEL), is_align = ALIGN_START gives the row of a video service: the items stand from the start edge (the right one, right to left), every one whole and level, and the last screen is full — no empty room after the last item; an item cut by an edge invites to scroll. The current item is no longer in the middle: it is the one the user chose, always whole on the screen. The arrows and Page Up / Page Down move a screen (the items it shows whole) and the current item becomes the first one the screen brought; the arrow keys move one item and scroll just enough to keep it whole; the wheel and a swipe scroll and rest on a whole item, the current item following only when it would leave; a click on a whole item makes it current and raises ue_item_clicked. The arrows live in the margins, show only under the pointer or with the keyboard, and are gone at an end. The dots count screens (a dot is a place the strip can rest on); the counter still counts items. The autoplay moves a screen and, at the end, the first screen comes back from the side. With ib_loop, the row goes round when it is long enough to go round the screen (about three items more than it shows). ALIGN_START has no effect on the other layouts.

// The row of a video service : the covers from the start edge, a screen at a time
uo_covers.is_style = u_pbt_carousel.STYLE_CAROUSEL
uo_covers.is_align = u_pbt_carousel.ALIGN_START

// Small covers : the row holds several screens
uo_covers.ii_item_height = 150

The item in front #

The current item is the one in front; of_current_key() reads its key live. A click on a neighbour brings it in front; a click on the item in front raises ue_item_clicked, a double click (or Enter) ue_item_opened. A change of the user — click, keyboard, wheel, swipe, dot, arrow, autoplay — and a change of your code (of_select_item, of_next, of_previous) raise the same ue_current_changed, as SelectItem raises the event of the click. With ib_veto_changes, ue_current_changing is asked first, in both cases: returning FALSE leaves the item in front, and of_select_item then returns -4. ue_current_changed is raised too when the item in front leaves (hidden, removed: the nearest takes its place) and when the first item arrives in an empty carousel (as_from_key empty). An order of your code inside ue_current_changing (another item selected) wins: the answer that follows changes nothing any more. of_select_row brings the item of a row in front and of_current_row reads the row of the item in front; with ib_sync_row, the DataWindow given to of_from_datastore follows the item in front — the master-detail of a DataWindow.

A greyed item (ib_enabled = false) stays in its place, but the user can neither bring it in front nor click it: the keyboard, the wheel, Page Up / Page Down, the scrollbar and a throw jump over it; your code still selects it. A hidden item (ib_visible = false) leaves the carousel and keeps its key and its place. With ib_loop (three items at least), the first comes after the last. A double click on a neighbour brings it in front and stops there: neither ue_item_clicked nor ue_item_opened. il_autoplay_ms makes the carousel move on by itself, every so many milliseconds (500 at least): a PAUSE / PLAY button shows under it (ib_autoplay_button) and the user's pause holds until PLAY (of_pause, of_play, ue_autoplay_changed); it also stops for a moment under the pointer, while the keyboard is in the component and while the carousel is hidden. At the end of a carousel that does not loop, the first item comes back from the side. Every step goes through ue_current_changing like a gesture: a refused item is skipped, the next one is asked. A screen reader is not told every step while it plays.


Cards and caption written by template #

A card shows its picture on top, its title (is_title), its subtitle (is_subtitle), its body (is_text), its tags (is_tags, separated by commas, coloured by is_tag_tones: New=success|Sale=danger) and a badge (is_badge). The body of every card can be written by a template, is_card_template, and the caption under the carousel by is_caption_template: rich text markup where {price} is a field of the item — one of yours (of_set_field), a column of the DataStore it came from, or a built-in field (key, title, subtitle, text, image, badge, tags, position, count). {position} and {count} count the items drawn, as the counter does. {sold:dd/mm/yyyy hh:mm} writes a date or a date-time, {price:#,##0.00} a number: the display formats of a DataWindow column (mm after an hour = the minutes, [shortdate], [longdate], [time], sections positive;negative;zero), in the display language — a value the format cannot write is shown as it is. {?x}…{/x} keeps a piece when the field says something, {!x}…{/x} when it says nothing, {{ }} are braces. A value is shown as it is, never read as markup; {x:raw} keeps it as markup — keep it for your own fields: on a database column it would read the data as markup, action zones included. {title}, {subtitle}, {text} and {badge} written by your code are markup, as in the caption; read from a column, text.

// A price, a field of our own, then the body of every card
uo_shop.of_set_field(/*key*/ "p1", /*field*/ "price", /*value*/ "$12.90")
uo_shop.is_card_template = "{?price}From [b]{price}[/b]{/price}{!price}Price on request{/price}"

// Under the carousel : the place of the item in front, then its title
uo_shop.is_caption_template = "{position} / {count} - [b]{title}[/b]"

From a DataStore #

of_from_datastore makes one item of each row of a DataStore, a DataWindow or a DataWindowChild — the same call. Its key is the RowID of the row — it names its row as long as the DataStore is neither emptied nor read again (Reset, Retrieve and ImportString give the RowIDs 1, 2, 3 again), and of_item(key).of_row() finds it — or the value of the ROLE_KEY column, the key to give as soon as you refresh. A row whose key is empty, not valid, already met or held by an item added by hand is not loaded: the call returns 0 and of_get_last_error names it — give your hand items keys that are not numbers. A column plays a role when it is named like it (key, title, subtitle, text, image, badge, tags, color) or when of_map says so; the ROLE_COLOR column (an RGB() or #rrggbb) paints the stripe of a card. of_map and of_reload_row update the items in place (flags, fields, handle and place stay). A second load too, with ROLE_KEY: an item whose key comes back keeps its flags, its fields and its handle. Without ROLE_KEY, a second load is a new epoch: a RowID that comes back may name another record, every item of the rows is made anew (nothing is inherited, the handles of the old ones are freed) and, if the key in front comes back, ue_current_changed says it with as_from_key = as_key. Either way, the items take the order of the rows (an of_move_item does not survive a load), the items of the rows gone leave, those added by hand stay, after the rows; an item of a row removed by of_remove_item comes back with its row — hide it instead (ib_visible). Dates are read in the regional order of the workstation, numbers are written with a point, CHAR values without their padding, at a load as after of_reload_row. What a column gives is text, never markup — an [action=x] coming from the database is not clickable —, and a picture of a column never reaches a network path (\\server\share: ue_image_failed with REASON_UNC). When the ROLE_KEY column of a reloaded row changed, the item takes the new key: the handle of the old one is freed. Every column is a field of the templates.

// The rows of the albums : columns named title and subtitle play their role
ids_albums.Retrieve()

// The picture is in a column with another name
uo_covers.of_map(/*role*/ u_pbt_carousel.ROLE_IMAGE, /*column*/ "cover_file")

// One item per row, its key the RowID of the row
uo_covers.of_from_datastore(/*ads*/ ids_albums)
// Master-detail : the DataWindow follows the item in front, and the carousel reads its rows
uo_covers.ib_sync_row = true
uo_covers.of_from_datastore(/*adw*/ dw_albums)

// event rowfocuschanged of dw_albums : the item of the row comes in front
uo_covers.of_select_row(/*row*/ currentrow)

Properties #

PropertyTypeDefaultPurpose
is_stylestringSTYLE_COVERFLOWThe layout: STYLE_COVERFLOW (the current item face on, its neighbours turned in perspective), STYLE_CAROUSEL (a flat strip, several items in full), STYLE_STACK (a stack of cards), STYLE_HERO (one item on the whole surface, a banner). Another value is refused: the old one stays, of_get_last_error says why
is_item_stylestringITEM_AUTOThe face of every item that does not choose one itself: ITEM_AUTO (from what it holds, see above), ITEM_IMAGE, ITEM_TEXT, ITEM_CARD. Another value is refused
is_fitstringFIT_COVERHow a picture fills its tile: FIT_COVER (the tile is full, the picture cropped) or FIT_CONTAIN (the whole picture). Another value is refused
ib_reflectionbooleanfalseA reflection under the items, as on a glossy floor
ib_loopbooleanfalseAfter the last item comes the first (and before the first, the last). It needs three items at least
il_autoplay_mslong0Autoplay: the next item comes in front every il_autoplay_ms milliseconds (500 at least, a smaller value is brought up to it); 0 stops it. A PAUSE / PLAY button shows under the carousel (ib_autoplay_button); it also stops for a moment under the pointer, while the keyboard is in the component and while the carousel is hidden. At the end of a carousel that does not loop, the first item comes back from the side; an item ue_current_changing refuses is skipped
ib_show_captionbooleantrueThe caption of the item in front: its title (and its subtitle), underlined with the accent, or what is_caption_template writes; it stands right under it or over its foot (is_caption_position)
is_caption_positionstringCAPTION_AUTOWhere the caption stands: CAPTION_BELOW (right under the item in front, under its reflection when it has one), CAPTION_OVERLAY (over its foot, white on a dark veil), CAPTION_AUTO (under a picture — over its foot in a banner —, and none under cards or texts that already write their title, unless is_caption_template says what to write). Another value is refused
ib_autoplay_buttonbooleantrueA PAUSE / PLAY button under the carousel while il_autoplay_ms runs, and a thin bar that fills until the next item: whoever reads may stop what moves. The user's pause holds until PLAY; ue_autoplay_changed says it. false: no button
is_indicatorstringINDICATOR_AUTOWhat shows where we are, under the caption: INDICATOR_AUTO (dots up to 12 items, a scrollbar beyond), INDICATOR_DOTS (clickable dots, up to 48 items: beyond, the scrollbar), INDICATOR_SCROLLBAR (a bar to click and drag), INDICATOR_COUNTER (3 / 12), INDICATOR_NONE. Another value is refused
ib_arrowsbooleantrueThe two arrow buttons on the sides: the left one always goes left
ii_item_widthinteger0The width of a tile, in pixels (24 to 4000, a value outside is brought back inside); 0: from the size of the component and is_item_ratio. A width set with a height of 0: the height follows the proportion. No effect on STYLE_HERO: its item takes the whole surface
ii_item_heightinteger0The height of a tile, in pixels (24 to 4000); 0: from the size of the component, leaving the neighbours and the caption in sight. A height set with a width of 0: the width follows the proportion. No effect on STYLE_HERO
is_item_ratiostring""The proportion of a tile, width:height: "1:1", "3:4", "16:9"… The tile keeps it whatever the size of the component. Empty: from the face of most items — 1:1 for pictures, 3:4 for cards, 16:9 for texts. A value that is not two positive numbers joined by : is refused. No effect on STYLE_HERO
ii_angleinteger62The turn of the covers on the sides of the Cover Flow, in degrees, from 0 (flat) to 80; 62 is the one of iTunes
ii_spacinginteger100The spacing of the covers of the Cover Flow and of the tiles of a strip, in percent of what the carousel works out: 100 spreads the covers over the width available, 25 to 300 tightens or loosens them
ib_shadebooleantrueThe covers on the sides darken with their distance, so that the item in front draws the eye; the cards waiting behind a stack too
is_transitionstringTRANSITION_SLIDEHow a banner (STYLE_HERO) changes its item: TRANSITION_SLIDE (the next one slides in from the side) or TRANSITION_FADE (it fades in). Another value is refused
is_alignstringALIGN_CENTERWhere the current item stands on a strip (STYLE_CAROUSEL): ALIGN_CENTER (in the middle) or ALIGN_START (the row of a video service, aligned at its start edge, moving a screen at a time: see above). No effect on the other layouts. Another value is refused
is_card_templatestring""The body of every card, written by a template (see above). Setting a template makes every ITEM_AUTO item a card. Empty: the text of the item (is_text)
is_caption_templatestring""The caption under the carousel, written by a template (the same fields). Empty: the title, and the subtitle under it
is_tag_tonesstring""The tone of each tag, tag=tone joined by `: New=successSale=danger. Tones: TONE_SUCCESS, TONE_WARN, TONE_DANGER, TONE_INFO (the accent), TONE_NEUTRAL`. A tag not listed, or an unknown tone, is neutral
is_badge_formatstring""The display format of the badges that are a number or a date — a ROLE_BADGE column of the DataStore, or a value your code writes: "$#,##0.00", "dd/mm/yyyy", the format of a DataWindow column, in the display language. A badge that is neither (markup, a word) shows as it is. Empty: every badge as it is
ib_sync_rowbooleanfalseMaster-detail: the DataWindow (or DataWindowChild) given to of_from_datastore follows the item in front — ScrollToRow on its row at every change, gesture or code. The other way round, call of_select_row from its RowFocusChanged. A DataStore has no row to show: nothing moves; an item added by hand has no row
ib_veto_changesbooleanfalseOpt-in: ask ue_current_changing before every change of the item in front, of the user, of the autoplay and of of_select_item alike. The autoplay skips a refused item; the keyboard stays where it is — an item always refused is better greyed. An order of your code inside ue_current_changing wins: the answer that follows changes nothing. Off by default, like every cancelable event of the library
ib_animatedbooleantrueThe items slide to their new places. A workstation set to reduce motion jumps straight there, unless the application insists (PBT_SetAnimationPolicy)
ii_anim_durationinteger450Duration of a move, in milliseconds (0 to 5000)
is_anim_easingstringEASING_EASE_OUTThe curve of the move: EASING_LINEAR, EASING_EASE_OUT, EASING_BACK_OUT (goes a little too far, then back), EASING_BOUNCE_OUT (bounces), EASING_ELASTIC_OUT (wobbles)
ib_enabledbooleantruefalse greys the carousel out: the items stay shown, nothing answers the user any more (click, keyboard, wheel, swipe, autoplay)
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

Its handle, through of_item(key). A read asks the carousel what it is now.

PropertyTypeDefaultPurpose
is_titlestring""Title of the item: the caption when it is in front, and the top line of a card. Accepts rich text markup. Empty, the item shows its key
is_subtitlestring""The second line, under the title of a card and of the caption (markup accepted). A picture with a subtitle stays an image: the subtitle goes to its caption
is_textstring""The text (markup accepted): the whole face of a text item, the body of a card while is_card_template is empty. An [action=x] zone raises ue_action_clicked on the item in front
is_imagestring""The picture: a file of the workstation, a resource file.dll:name, a mono: / tint: glyph drawn in the theme, an https:// address. http:// is refused (ue_image_failed, REASON_INSECURE), and so is a network path from a DataStore column (REASON_UNC). A themed skeleton shows while the picture is on its way
is_badgestring""Small badge (markup accepted): a price, a count, in the accent — at the top right of a card, over the corner of a picture. A picture with a badge stays an image
is_tagsstring""Tags of a card, separated by commas: Vinyl, Sale. Each one is a chip coloured by is_tag_tones of the carousel
is_stylestring""The face of this item (ITEM_* of u_pbt_carousel); empty: it follows is_item_style of the carousel. Another value is refused: the old one stays, of_get_last_error of the carousel says why
ib_visiblebooleantruefalse takes the item out of the carousel; it keeps its key and its place. When it was in front, the nearest one takes its place (ue_current_changed)
ib_enabledbooleantruefalse greys the item: the user can neither bring it in front nor click it, the keyboard and the wheel jump over it. Your code still selects it
of_position ( ) → long——Returns the place of the item in the carousel now, counted from 1 (hidden items included); 0 when the carousel does not hold it
of_row ( ) → long——Returns the row number today, in the rows given to of_from_datastore, of the row the item comes from — after a sort, a filter or the deletion of other rows, as long as the DataStore is neither emptied nor read again; 0 for an item added by hand or whose row is gone
of_face ( ) → string——The face the item is drawn with now: "image", "text" or "card" (ITEM_IMAGE, ITEM_TEXT, ITEM_CARD) — what is_style chose or what ITEM_AUTO worked out; empty when the carousel does not hold it
is_tooltipstring""Simple tooltip shown when hovering the item
is_super_tooltip_titlestring""Title of the item rich tooltip (takes precedence over is_tooltip)
is_super_tooltip_textstring""Text of the item rich tooltip (rich markup accepted)
is_super_tooltip_imagestring""Image of the item rich tooltip
il_accentlong-1Accent of the item: the stripe at the top of a card (which it brings up) and of a text panel, and the badge, take it; it wins over the colour of a ROLE_COLOR column. The dots and the caption keep the accent of the component. -1 = the one of the component
il_back_colorlong-1Background of a text or card face, as RGB(r,g,b) (-1 = what the theme gives). An image face has no background
il_text_colorlong-1Text colour of a text or card face (-1 = what the theme gives)
il_back_color_hoverlong-1Background of a text or card face under the pointer, in RGB(r,g,b) (-1 = its usual background). Nothing moves on hover : only the colour changes
il_text_color_hoverlong-1Text colour of a text or card face under the pointer (-1 = its usual colour)

Three reads of the handle, read only: of_position() returns the place of the item counted from 1, of_row() the number of its row in the DataStore (0 for an item added by hand), of_face() the face chosen. An item moves through of_move_item of the carousel.


Methods #

MethodPurpose
of_add_item (string as_key, string as_title)Adds an item at the end of the carousel, a title alone (a card): its picture, text, subtitle… are set on its handle. Its key is unique in the carousel. The first item of an empty carousel comes in front (ue_current_changed). Returns 0 once added, -5 for an empty key, one holding / or `, starting with __, or already taken (an item of a DataStore included), -2` when the component is not created
of_add_item (string as_key, string as_title, string as_image)The same with its picture. With ITEM_AUTO, a picture and a title alone make an image item, the title as its caption. Returns 0 once added, -5 as above, -2 when the component is not created
of_clear ( )Empties the carousel: items (those of a DataStore included) and their handles; the DataStore stays bound for a new of_from_datastore, the options stay. ue_current_changed follows, with an empty key, when an item was in front. Returns 0 once applied, -2 when the component is not created
of_current_key ( )The key of the item in front, whoever brought it there; empty when the carousel is empty. Read live
of_current_row ( )The row number today, in the rows given to of_from_datastore, of the item in front — after a sort, a filter or the deletion of other rows. Returns 0 when the carousel is empty, when the item in front was added by hand or its row is gone
of_from_datastore (datastore ads)Makes an item of each row of the DataStore (see above). A second load updates the items in place with ROLE_KEY, makes them anew without it (a new epoch: nothing is inherited); those of the rows gone leave (handles freed), those added by hand stay, after the rows; a row not loaded is named by of_get_last_error. Returns 0 once loaded, -5 when the DataStore is not valid, -2 when the component is not created
of_from_datastore (datawindow adw)The same from the rows of a DataWindow, read through a DataStore that shares them (ShareData). Returns 0 once loaded, -5 when the DataWindow is not valid or refuses to share its rows (of_get_last_error says it), -2 when the component is not created
of_from_datastore (datawindowchild adwc)The same from the rows of a DataWindowChild (a drop-down DataWindow, a nested report). Returns 0 once loaded, -5 when it is not valid, -2 when the component is not created
of_get_field (string as_key, string as_field)Reads a field of an item, live: one of yours, a column of its DataStore or a built-in field (title, position, count…). An empty string when it has none
of_get_last_error ( )Why the last call went wrong: a method that returned -5 or -4, a value refused (is_style, is_item_style, is_indicator, is_fit, is_caption_position, is_transition, is_item_ratio, is_align, is_style of an item), a call made during a wait (nothing could be judged), the rows a load left out. Emptied by every method that acts (a read leaves it); otherwise the last error of the library
of_insert_item (string as_key, string as_title, string as_image, long al_position)Adds an item at a place counted from 1 (past the last one: at the end). The item in front stays in front. Returns 0 once added, -5 like of_add_item, -2 when the component is not created
of_is_playing ( )TRUE while the autoplay plays: il_autoplay_ms is set and nobody pressed PAUSE (a pointer over the carousel suspends it for a moment: it still plays). Read live
of_item (string as_key)Returns the handle of an item (created on first access, freed when the item is removed)
of_map (string as_role, string as_column)Gives a column of the DataStore a role when its name does not say it (ROLE_KEY, ROLE_TITLE, ROLE_SUBTITLE, ROLE_TEXT, ROLE_IMAGE, ROLE_BADGE, ROLE_TAGS, ROLE_COLOR). Before or after of_from_datastore: after, the rows are read again in place. An empty column gives the role back to the column named like it; a new ROLE_KEY gives the items new keys (the handles of the old ones are freed). Returns 0 once set, -5 for an unknown role or a column the DataStore does not have, -2 when the component is not created
of_move_item (string as_key, long al_position)Moves an item to a place counted from 1 (past the last one: the last). The item in front stays in front. Returns 0 once moved, -5 for an unknown item, -2 when the component is not created
of_next ( )Brings the next item in front (the greyed ones skipped; after the last, the first with ib_loop), like the arrow key toward it (Right, Left right to left), and raises ue_current_changed. Returns 0 once done (also at the end: nothing moves, nothing is raised), -4 when ue_current_changing refused, -5 during a wait (nothing could be judged), -2 when the component is not created
of_pause ( )Pauses the autoplay, like the PAUSE button: the item in front stays until of_play (or PLAY). ue_autoplay_changed follows — an order of the code raises it like the button. Returns 0 once paused (also when it already is: nothing is raised), -5 when il_autoplay_ms is 0, -2 when the component is not created
of_play ( )Plays the autoplay again after a pause — the user's PAUSE button or of_pause. ue_autoplay_changed follows. Returns 0 once playing (also when it already plays: nothing is raised), -5 when il_autoplay_ms is 0 (nothing to play), -2 when the component is not created
of_previous ( )Brings the previous item in front, like the arrow key toward it. Returns 0 once done (also at the start: nothing moves, nothing is raised), -4 when ue_current_changing refused, -5 during a wait, -2 when the component is not created
of_reload_row (long al_row)Sends a row of the DataStore of of_from_datastore to the carousel again, read now, after your code changed it (SetItem, an edit window); its item is updated in place (flags, fields, handle and place stay). A row the carousel does not hold yet (InsertRow) arrives after the last row item. When the ROLE_KEY column of the row changed, the item takes the new key: the handle of the old one is freed and, if it stood in front, ue_current_changed says it. Returns 0 once sent, -5 when nothing is bound, the row does not exist or its key is empty, not valid or held by another item (of_get_last_error says it), -2 when the component is not created
of_remove_item (string as_key)Removes an item and frees its handle; removing the item in front brings the nearest one in front (ue_current_changed). An item of a DataStore row comes back with its row (of_reload_row, a new load): hide it (ib_visible) or delete the row. Returns 0 once removed, -5 for an unknown item, -2 when the component is not created
of_reset ( )Empties the carousel, forgets the DataStore and puts every property back to its default. Returns 0 once applied, -2 when the component is not created
of_select_item (string as_key)Brings an item in front, like a click: it slides there and ue_current_changed follows. With ib_veto_changes, ue_current_changing is asked first. A greyed item is selected too: greyed stops the user, not your code. Returns 0 once done (or already in front: nothing is raised), -4 when ue_current_changing refused, -5 for an item unknown or not drawn (hidden, beyond the demo cap), -2 when the component is not created
of_select_row (long al_row)Brings in front the item of a row of the rows given to of_from_datastore — al_row is the row number today, whatever ROLE_KEY says: the master-detail of a DataWindow, called from its RowFocusChanged. Like of_select_item on its key: ue_current_changed follows, ib_veto_changes asks first. Returns 0 once in front (or already in front), -4 when ue_current_changing refused, -5 when nothing is bound, the row does not exist or made no item (left out by the load, removed, hidden, beyond the demo cap), -2 when the component is not created
of_set_field (string as_key, string as_field, string as_value)Gives an item a field of your own, for the templates: {price}; it wins over a DataStore column of the same name. A field name: letters, digits and _, no leading digit. Returns 0 once set, -5 for an unknown item or a name that is not one, -2 when the component is not created

Events #

EventRaised when
ue_current_changed (string as_from_key, string as_key)The item in front changed: a click on a neighbour, the keyboard, the wheel, a swipe, a dot, an arrow, the autoplay, or your code (of_select_item, of_next, of_previous). Also when the item in front leaves (hidden, removed: the nearest takes its place) when the first item arrives in an empty carousel (as_from_key empty), and when a new load without ROLE_KEY gives the key in front to another record (as_from_key = as_key). as_key empty: the carousel is empty. With ib_sync_row, the DataWindow bound already shows the row
ue_current_changing (string as_from_key, string as_key) → booleanCancelable, asked before a change of the item in front, only when ib_veto_changes = true: gesture, autoplay and of_select_item (so of_next, of_previous). Returning FALSE leaves the item in front, and of_select_item then returns -4. By default: TRUE
ue_item_clicked (string as_key)The item in front was clicked (or Space pressed). A click on a neighbour brings it in front instead (ue_current_changed) — and a double click on a neighbour only brings it, without this event; an [action=x] zone raises ue_action_clicked instead; a greyed item raises nothing
ue_item_opened (string as_key)A double click on the item in front, or Enter: open it. A double click on a neighbour only brings it in front
ue_item_rclicked (string as_key, long al_x, long al_y)A right click on an item, or the context-menu key (or Shift + F10) on the item in front. al_x, al_y are screen pixels; for a PowerBuilder menu, PopMenu(PointerX(), PointerY()) of your window
ue_action_clicked (string as_key, string as_action)An [action=x] zone of the text of the item in front was clicked: as_action = "x"
ue_autoplay_changed (boolean ab_playing)The autoplay was paused (ab_playing = false) or played again (true): the PAUSE / PLAY button under the carousel, or your code (of_pause, of_play raise it too). A pointer over the carousel suspends it without raising it
ue_image_failed (string as_key, string as_reason)The picture of an item could not be shown: as_reason is REASON_INSECURE (an http:// address), REASON_UNC (a network path from a DataStore column, refused before any request) or REASON_NOT_FOUND (file not found). Said once per item and picture
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)

Keyboard and mouse #


Examples #

A product showcase #

// A flat strip : several products in full, a counter under it
uo_shop.is_style = u_pbt_carousel.STYLE_CAROUSEL
uo_shop.is_indicator = u_pbt_carousel.INDICATOR_COUNTER

// A product : a picture and a title, then a badge and tags (it becomes a card)
uo_shop.of_add_item(/*key*/ "p1", /*title*/ "Northern Lights", /*image*/ "img\packimages.dll:SVG/COVERS/COVER-01")
uo_shop.of_item(/*key*/ "p1").is_badge = "New"
uo_shop.of_item(/*key*/ "p1").is_tags = "Vinyl, Sale"

// The tone of each tag, and the price in the body of every card
uo_shop.is_tag_tones = "Vinyl=info|Sale=danger"
uo_shop.of_set_field(/*key*/ "p1", /*field*/ "price", /*value*/ "$12.90")
uo_shop.is_card_template = "From [b]{price}[/b]"

Asking before the item changes #

// Once : ask before the item in front changes
uo_covers.ib_veto_changes = true

// event ue_current_changing of uo_covers : not while the form beside it is not saved
if ib_unsaved then return false
return true

Opening an item of a DataStore in your window #

// Local variables
long ll_row

// event ue_item_opened of uo_covers : the row of the item, edited in a RESPONSE window (the code waits for it)
ll_row = uo_covers.of_item(/*key*/ as_key).of_row()
OpenWithParm(w_album_edit, ll_row)

// The row may have changed : the item follows it
uo_covers.of_reload_row(/*row*/ ll_row)

News that move on by themselves #

// A banner : one item on the whole surface
uo_news.is_style = u_pbt_carousel.STYLE_HERO

// Text items : rich text on a themed panel, with an [action] zone
uo_news.of_add_item(/*key*/ "n1", /*title*/ "Release")
uo_news.of_item(/*key*/ "n1").is_text = "[b]Version 5.0[/b] is out. [action=notes]Read the notes[/action]"
uo_news.of_add_item(/*key*/ "n2", /*title*/ "Webinar")
uo_news.of_item(/*key*/ "n2").is_text = "[b]Webinar[/b] on Thursday at 10:00."
uo_news.of_add_item(/*key*/ "n3", /*title*/ "Support")
uo_news.of_item(/*key*/ "n3").is_text = "[b]Support[/b] answers within one day."

// A new one every 5 seconds, faded in, the first again after the last :
// a PAUSE button shows under the banner
uo_news.is_transition = u_pbt_carousel.TRANSITION_FADE
uo_news.il_autoplay_ms = 5000
uo_news.ib_loop = true
// event ue_action_clicked of uo_news : (string as_key, string as_action)
// The zone [action=notes] of the first item opens the release notes
if as_action = "notes" then Open(w_release_notes)

A large catalogue #

// The products : one row = one item, its key the product code
ids_products.Retrieve()
uo_shop.of_map(/*role*/ u_pbt_carousel.ROLE_KEY, /*column*/ "code")
uo_shop.of_from_datastore(/*ads*/ ids_products)

// Only the tiles near the one in front are drawn : thousands of rows cost nothing.
// Beyond 12 items a scrollbar replaces the dots ; the user types a name to reach it
uo_shop.of_select_item(/*key*/ "P250")

Good practice #

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
is_accessible_name · is_accessible_descriptionThe name and description a screen reader says3.4 Optional properties and events

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