PBToolboxAI v5 ← Site

kanban — u_pbt_kanban #

← Component reference · Guide contents

kanban is a board of cards sorted into columns — the stages of a piece of work: to do, in progress, in review, done. The user drags a card from one column to another, or up and down in its own; ue_card_moved tells you which one, from where, to where and at which place. Each card shows a title, coloured tags, its assignee, a due date that turns red when late and a badge; each column has its work-in-progress limit and folds. The board is filled by hand or from a DataStore, which receives the moves.

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


At a glance #

Userobjectu_pbt_kanban
Item classesn_pbt_kanban_column (of_column(key)), n_pbt_kanban_card (of_card(key))
Used forFollowing a flow of work: tickets, tasks, orders, applications
PrincipleA card has a key unique on the whole board; its column is a state that changes at every move
Opt-in optionsib_veto_moves, ib_write_back, ib_reorderable, ib_toolbar, ib_add_button, ib_quick_add

Quick start #

// The columns, in the order of the work
uo_board.of_add_column(/*key*/ "todo", /*title*/ "To do")
uo_board.of_add_column(/*key*/ "doing", /*title*/ "In progress")
uo_board.of_add_column(/*key*/ "done", /*title*/ "Done")

// The cards : a key unique on the board, a column, a title
uo_board.of_add_card(/*key*/ "t1", /*column*/ "todo", /*title*/ "Login page")
uo_board.of_add_card(/*key*/ "t2", /*column*/ "doing", /*title*/ "Crash on save")
uo_board.of_card(/*key*/ "t2").is_tags = "Bug"
uo_board.of_card(/*key*/ "t2").is_assignee = "Alan Turing"
// event ue_card_moved of uo_board : (string as_key, string as_from_column,
// string as_to_column, long al_position, long al_from_position)
// Save the new state of the task
UPDATE tasks SET status = :as_to_column WHERE id = :as_key;

Columns, cards and places #

A column is named by its key, a card too — and the key of a card is unique on the whole board, not only in its column: a card keeps its key when it changes column, its handles stay valid and every event names it alone. The column of a card is read by of_card(key).of_column(), its place by of_position(); a place counts from 1, hidden cards included.

A column has a work-in-progress limit (il_limit): its header counts 3/5, and it turns red past it. The limit is soft — the card lands anyway; for a hard limit, refuse the move in ue_card_moving by comparing il_limit with of_visible_count() of the column, what the header counts (a hidden card does not count). ib_done marks the column of finished work: its cards are never late. A column folds into a narrow strip (its chevron, ib_collapsed); a card still drops on it, at the end.

A move of the user (a drag, Ctrl + arrows, or the card lifted with Space and dropped) and a move of your code (of_move_card) raise the same ue_card_moved, as SelectItem raises the event of the click. With ib_veto_moves, ue_card_moving is asked first, in both cases: returning FALSE leaves the card where it is, and of_move_card then returns -4.

With ib_reorderable = true, the user reorders the columns too: he drags the header of a column (a vertical accent bar shows where it will land, Escape cancels) or presses Ctrl + Shift + Left / Right. ue_column_moved gives the column and its two places, counted from 1 among all the columns; of_move_column does the same from your code and raises the same event, ue_column_moving being asked first with ib_veto_moves. Off by default: the columns are the process of your application. What the user arranged — the order of the columns and their fold — is read by of_get_layout, given back by of_set_layout, and every change raises ue_layout_changed with the whole layout: store it, give it back at the next start.


What a card shows #

A card shows its title (is_title), its text (is_text, cut after four lines), its tags (is_tags, separated by commas, coloured by is_tag_tones: Bug=danger|UI=info), its assignee (is_assignee, a round avatar with the initials), its due date (is_due, yyyy-mm-dd: red once past, orange on the day, compared with is_today or the date of the workstation) and a badge (is_badge). Title, text and badge accept the rich text markup.

The body of the cards can be written by a template, is_card_template: rich text where {customer} is a field of the card — one of yours (of_set_field), a column of the DataStore it came from, or a built-in field (title, text, tags, assignee, initials, badge, due, column, column_title, key). {due:dddd d mmmm} writes a date as a DataWindow display format does (the tokens d dd ddd dddd m mm mmm mmmm yy yyyy, a text between double quotes — outside quotes, a letter d, m or y is a token —, [shortdate] and [longdate] in the language of the display), {?amount}…{/amount} keeps a piece when the field says something, {!amount}…{/amount} when it says nothing, {x:raw} keeps a value as markup, {{ }} are braces. A value is always shown as it is, never read as markup.

// A field of our own, then the body of every card
uo_board.of_set_field(/*key*/ "t1", /*field*/ "customer", /*value*/ "Acme")
uo_board.is_card_template = "{?customer}For [b]{customer}[/b]{/customer}{?due} - due {due:d mmm}{/due}"

From a DataStore #

of_from_datastore makes one card of each row of a DataStore, a DataWindow or a DataWindowChild — the same call, and a DataWindow is written back itself. Its key is the RowID of the row — of_card(key).of_row() gives the number of its row as long as the DataStore is neither reset nor retrieved again — or the value of the ROLE_KEY column. A row whose key is empty, not valid, already met or held by a card added by hand is not loaded: the call returns 0 and of_get_last_error names it by its number — give your hand cards keys that are not numbers. A column plays a role when it is named like it (title, text, tags, assignee, due, badge, color, order, column, key) or when of_map says so. The ROLE_COLUMN column says in which column of the board the card stands; a value no column holds gets a column of its own, at the end — a value holding / or | takes a key where they become - (R&D-QA) and keeps its value, the one a move writes back; an empty value is not loaded (of_get_last_error names it), a CHAR value padded with blanks finds its column. A column born of a value goes once empty, unless your code set a property or a colour on it. ROLE_ORDER sorts the cards of a column: the rank is the place of a card among the cards of the DataStore of its column, from 1 (a card added by hand takes no rank). Keyed by ROLE_KEY, a second load, of_map and of_reload_row update the cards in place: a card that comes back keeps its flags, its fields and its handle. Keyed by the RowID, a second of_from_datastore is a new load: PowerBuilder gives the same RowIDs again after a Reset or a Retrieve, to other records — every card of the old rows goes (its handle is freed) and the cards are made anew; of_map and of_reload_row stay in place. A card your code removed does not come back with of_map: its row stays in the DataStore, the next of_from_datastore reads it again. Dates and decimals are read in the regional settings of the workstation (a decimal field reads 1234.5). Every column is a field of the template.

With ib_write_back = true, a card of a row writes its new column into the row (and, with ROLE_ORDER, the ranks of both columns) before it moves — the row is found by its RowID and by nothing else: ue_card_moved reads the DataStore up to date, the Update stays yours. The column receives the value the target column stands for (R&D/QA, never its key R&D-QA). All or nothing: a value the DataStore refuses — the column or a rank — puts back what was already written, leaves the card where it was (it never leaves to come back) and raises ue_write_back_failed; of_move_card then returns -4. One move of the user is written at a time: a gesture made before the previous one is written is ignored, and said. After changing a row yourself, of_reload_row sends it to the board again.

// The columns of the board : the values of the status column
uo_board.of_add_column(/*key*/ "open", /*title*/ "Open")
uo_board.of_add_column(/*key*/ "active", /*title*/ "Active")
uo_board.of_add_column(/*key*/ "closed", /*title*/ "Closed")

// Which column of the DataStore plays which role
uo_board.of_map(/*role*/ u_pbt_kanban.ROLE_COLUMN, /*column*/ "status")
uo_board.of_map(/*role*/ u_pbt_kanban.ROLE_ASSIGNEE, /*column*/ "owner")
uo_board.of_map(/*role*/ u_pbt_kanban.ROLE_ORDER, /*column*/ "rank")

// A move goes into the DataStore, then the rows become cards
uo_board.ib_write_back = true
uo_board.of_from_datastore(/*ads*/ ids_tasks)

Properties #

PropertyTypeDefaultPurpose
is_card_templatestring""The body of every card, written by a template (see above). Empty: the text of the card (is_text). The search reads what the template writes
is_count_templatestring""The count in the header of every column, written by a template: {count} the visible cards, {limit} the limit (empty without one), {shown} the cards the search keeps, {sum:points} the sum of a field over the visible cards (one of yours or a column of the DataStore), {title} and {key} of the column; {?limit}…{/limit} keeps a piece when there is a limit. Empty: 3, 3/5, 1 . 3/5 during a search. Past its limit the column turns red whatever the template says, and its header says it to a screen reader. {sum:} reads a number written with a dot or in the language of the display (1,250 in English); a text counts for nothing. Without a key, the count and the sum are those of the cards the demo draws
is_tag_tonesstring""The tone of each tag, tag=tone joined by `: Bug=dangerUI=info. Tones: TONE_SUCCESS, TONE_WARN, TONE_DANGER, TONE_INFO (the accent), TONE_NEUTRAL`. A tag finds its tone whatever its case; a tag not listed, or an unknown tone, is neutral
is_todaystring""The day the due dates are compared with, yyyy-mm-dd; empty: the date of the workstation. A date that is not one is refused: the old one stays, of_get_last_error says why
is_filterstring""The search: only the cards whose title, body, tags, assignee, badge, key, a field of yours or a column of the DataStore contain this text stay, whatever the case and the accents. The count of a column then says the cards found first (1 . 3). The search box of the toolbar writes it too: read back live
ib_toolbarbooleanfalseA toolbar above the board, with a search box that writes is_filter (Escape empties it)
ib_add_buttonbooleanfalseAn + Add a card button at the foot of every open column: it raises ue_new_requested. A double click on the empty part of a column raises it too, button or not
ib_quick_addbooleanfalseQuick add: the + Add a card button and the Insert key open a title field at the foot of the column; Enter raises ue_new_requested with the title typed (an empty title asks nothing), the field stays open for the next card, Escape closes it. The board still adds nothing: your code adds the card
ib_read_onlybooleanfalseThe user moves nothing: no drag, no Ctrl + arrows, no Delete, no add button. He still selects and opens the cards; your code still moves them
ib_reorderablebooleanfalseThe user reorders the columns: he drags their header or presses Ctrl + Shift + Left / Right; ue_column_moved then ue_layout_changed follow. ib_read_only stops it too; your code still moves them (of_move_column). Read back live
ib_veto_movesbooleanfalseOpt-in: ask ue_card_moving before every move, of the user and of of_move_card alike. Off by default, like every cancelable event of the library
ib_write_backbooleanfalseA board fed by of_from_datastore: a card of a row writes its new column (and the ranks, with ROLE_ORDER) into the DataStore before it moves; a value refused leaves it in place (ue_write_back_failed, of_move_card returns -4). A card added by hand is never written
ii_column_widthinteger272The width of an open column, in pixels, from 180 to 600 (a value outside is brought back inside); 0: the open columns share the width of the board, 180 pixels at least each. The board scrolls sideways when the columns do not fit
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

Properties of a column — n_pbt_kanban_column #

Its handle, through of_column(key). Its of_count() counts the cards it holds and its of_keys_at(n) returns the key of the n-th, in their order; of_child_keys returns them all in one read.

PropertyTypeDefaultPurpose
is_titlestring""Title of the column. Accepts rich text markup. Empty, the column shows its key
il_limitlong0Work-in-progress limit: 0, none. With a limit the header says 3/5, and the column turns red once it holds more; while a card is dragged over a full column, it shows it. Soft limit: refuse in ue_card_moving for a hard one
ib_collapsedbooleanfalsetrue folds the column into a narrow strip: its title written downwards and its count stay, its cards hide, a card still drops on it (at the end). ue_column_toggled follows, as for a click on its chevron. Read back live
ib_visiblebooleantruefalse takes the column and its cards out of the board; they keep their place and their order
ib_donebooleanfalseThe column of finished work: its cards are never shown late nor due today
of_keys_at (long al_index) → string——The key of the card at rank al_index (from 1) in the column, in their order; empty past either end. A card being named by its key alone, this is what of_card takes
of_child_keys (ref string as_keys[]) → long——Fills as_keys[] with the keys of every card of the column, in their order — what of_keys_at(1..n) gives — and returns their count, in one read: walking 2,000 cards with of_keys_at reads the list 2,000 times
of_position ( ) → long——Returns the place of the column on the board now, counted from 1 among all the columns (hidden ones included) — the user may have moved it; 0 when the board does not hold it
of_visible_count ( ) → long——Returns how many cards of the column are visible, what its header counts against il_limit (2/3): a card hidden by ib_visible does not count, a card out of the search does; of_count() counts them all. The number to compare with il_limit for a hard limit; 0 when the board does not hold the column
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 column: the bar on top of it and its count take it, never its cards (-1 = the one of the component)
il_back_colorlong-1Background of the column, as RGB(r,g,b) (-1 = what the theme gives)
il_text_colorlong-1Text colour of the column (-1 = what the theme gives)
il_back_color_hoverlong-1Background of the header of the column under the pointer (-1 = what the theme gives). The colours of a column stop at its cards
il_text_color_hoverlong-1Text colour of the header of the column under the pointer (-1 = what the theme gives)

Properties of a card — n_pbt_kanban_card #

PropertyTypeDefaultPurpose
is_titlestring""Title of the card, in bold. Accepts rich text markup. Empty, the card shows its key
is_textstring""Text under the title, cut after four lines (markup accepted). Shown while is_card_template is empty
is_tagsstring""Tags, separated by commas: Bug, UI. Each one is a chip coloured by is_tag_tones
is_assigneestring""The person the card is given to: a round avatar with the initials, of the same colour at every load, the name as tooltip
is_duestring""Due date, yyyy-mm-dd ("": none): red once past, orange on the day, never in an ib_done column. A date that is not one is refused: the old one stays, of_get_last_error of the board says why
is_badgestring""Small badge at the top right (markup accepted): an estimate, a number, in the accent
ib_visiblebooleantruefalse takes the card out of the board; it keeps its key, its column and its place, and no longer counts in its column
ib_read_onlybooleanfalseThe user can neither drag this card, nor move it with the keyboard, nor ask to delete it; your code still moves it
of_column ( ) → string——The key of the column the card stands in now — the user may have moved it. Read only: a card moves through of_move_card
of_position ( ) → long——Returns the place of the card in its column now, counted from 1 (hidden cards included), 0 when the board does not hold it
of_row ( ) → long——Returns the row number today, in the rows given to of_from_datastore, of the row the card comes from — after a sort, a filter or the deletion of other rows; 0 for a card added by hand or whose row is gone
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 card: a stripe on its leading edge, its selection ring and its badge take it; it wins over the colour of a ROLE_COLOR column. -1 = the one of the component, no stripe
il_back_colorlong-1Background of the card, as RGB(r,g,b) (-1 = what the theme gives)
il_text_colorlong-1Text colour of the card (-1 = what the theme gives)
il_back_color_hoverlong-1Background of the card under the pointer (-1 = what the theme gives)
il_text_color_hoverlong-1Text colour of the card under the pointer (-1 = what the theme gives)

Three reads of the handle, read only: of_column() returns the key of the column the card stands in now, of_position() its place in that column counted from 1 (0 when the board does not hold it), of_row() the number of its row in the DataStore (0 for a card added by hand). A card moves through of_move_card of the board.


Methods #

MethodPurpose
of_add_card (string as_key, string as_column, string as_title)Adds a card at the bottom of a column. Its key is unique on the whole board. Returns 0 once added, -5 for an empty key, one holding / or `, one already taken (a card of a DataStore included), or an unknown column, -2` when the component is not created
of_add_column (string as_key, string as_title)Adds a column at the right of the board. Returns 0 once added, -5 for an empty key, one holding / or `, or one already taken, -2` when the component is not created
of_card (string as_key)Returns the handle of a card (created on first access, freed when the card is removed)
of_clear ( )Empties the board: columns, cards (those of a DataStore included) and their handles; the options stay. Returns 0 once applied, -2 when the component is not created
of_clear_cards ( )Removes every card and its handle, keeps the columns: to load the cards again. Returns 0 once applied, -2 when the component is not created
of_clear_selection ( )Leaves no card selected: ue_selection_changed follows with an empty key; nothing selected, nothing is raised. Returns 0 once applied, -2 when the component is not created
of_column (string as_key)Returns the handle of a column (created on first access, freed when the column is removed)
of_from_datastore (datastore ads)Makes a card of each row of the DataStore (see above). A second load updates the cards in place with a ROLE_KEY key, makes them anew with the RowID key; the cards of the rows gone leave, those added by hand stay; a row not loaded is named by of_get_last_error. Returns 0 once loaded, -5 when the DataStore is not valid or has no column, -2 when the component is not created
of_from_datastore (datawindow adw)The same from the rows of a DataWindow — the one the user sees beside it: ib_write_back writes into this DataWindow. Returns 0 once loaded, -5 when the DataWindow is not valid, has no column or cannot share its rows (ShareData), -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), written back by ib_write_back. Returns 0 once loaded, -5 when it is not valid, has no column or cannot share its rows (ShareData), -2 when the component is not created
of_get_field (string as_key, string as_field)Reads a field of a card, live: one of yours, a column of its DataStore or a built-in field (column_title, initials…). 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 date refused (is_due, is_today), the rows a load left out, by their number. Emptied by every call; otherwise the last error of the library
of_get_layout ( )What the user arranged, as JSON: {"columns":[{"key":"todo","collapsed":false},...]} — every column in its order (hidden ones included), with its fold. To store, then give back to of_set_layout. Titles, limits and cards are yours: not in it. Read live
of_map (string as_role, string as_column)Gives a column of the DataStore a role (ROLE_KEY, ROLE_COLUMN, ROLE_TITLE, ROLE_TEXT, ROLE_TAGS, ROLE_ASSIGNEE, ROLE_DUE, ROLE_BADGE, ROLE_COLOR, ROLE_ORDER). After of_from_datastore, the rows are read again in place: a moved card stays where it was dropped and keeps its flags and its handle. An empty column gives the role back to the column named like it. A card your code removed does not come back. 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_card (string as_key, string as_column, long al_position)Moves a card to a column, at a place counted from 1 (past the last one: at the end), like a drag: the question with ib_veto_moves, then ue_card_moved, and, with ib_write_back, the DataStore written before the move. Returns 0 once moved (or already there: nothing is raised), -4 when ue_card_moving refused, the DataStore refused the new column or a rank (the card stays, ue_write_back_failed, of_get_last_error says why) or the board is waiting for the page (a call from a timer during a read: neither the question nor the write can be made), -5 for an unknown card or column, -2 when the component is not created
of_move_column (string as_key, long al_position)Moves a column to a place counted from 1 among all the columns, hidden ones included (past the last one: the last), like a drag of its header: the question with ib_veto_moves, then ue_column_moved and ue_layout_changed. Neither ib_reorderable nor ib_read_only stop your code. Returns 0 once moved (or already there: nothing is raised), -4 when ue_column_moving refused, -5 for an unknown column, -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 board again, read now, after your code changed it; its card is updated in place (flags, fields and handle stay) and follows its column and its rank. Returns 0 once sent, -5 when nothing is bound, the row does not exist, its key is held by a card added by hand, its column value is empty or its ROLE_KEY value is no longer the key of its card — a key is an identity: after an Update that gives the keys, load again with of_from_datastore (of_get_last_error says it, nothing changed), -2 when the component is not created
of_remove_card (string as_key)Removes a card and frees its handle; removing it while selected clears the selection. Returns 0 once removed, -5 for an unknown card, -2 when the component is not created
of_remove_column (string as_key)Removes a column and all its cards, with their handles. Returns 0 once removed, -5 for an unknown column, -2 when the component is not created
of_reset ( )Empties the board, forgets the DataStore and puts every property back to its default. Returns 0 once applied, -2 when the component is not created
of_select_card (string as_key)Selects a card like a click, without moving the view (like SelectRow of a DataWindow), and ue_selection_changed follows. Returns 0 once selected (or already selected: nothing is raised), -5 for an unknown card or one not drawn (hidden, in a folded or hidden column, out of the search, beyond the demo cap), -2 when the component is not created
of_selected_key ( )The key of the selected card, whoever selected it; empty if none. Read live
of_set_field (string as_key, string as_field, string as_value)Gives a card a field of your own, for is_card_template; 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 card or a name that is not one, -2 when the component is not created
of_set_layout (string as_layout_json)Gives back a layout read by of_get_layout or received by ue_layout_changed: the columns it names in its order, then the others in theirs, each with its fold; a column the board does not hold is ignored. ue_layout_changed is raised once when something changed (neither ue_column_moved nor ue_column_toggled: it puts a state back). Returns 0 once applied, -5 when the text is empty or is not a JSON object, -2 when the component is not created

Events #

EventRaised when
ue_card_moved (string as_key, string as_from_column, string as_to_column, long al_position, long al_from_position)A card moved: dragged, moved with the keyboard, or by your code (of_move_card). The two column keys (the same one for a move inside a column), al_position its place in the target column and al_from_position the place it left, counted from 1. The card is already in place; with ib_write_back, the DataStore is already written
ue_card_moving (string as_key, string as_from_column, string as_to_column, long al_position) → booleanCancelable, asked before a move, only when ib_veto_moves = true: drag, keyboard and of_move_card. Returning FALSE leaves the card where it is. The place for a hard limit: of_column(as_to_column).of_visible_count() >= il_limit
ue_card_clicked (string as_key)A card was clicked (and selected). Not at the end of a drag
ue_card_opened (string as_key)A double click on a card, or Enter: open your editor
ue_card_rclicked (string as_key, long al_x, long al_y)A right click on a card (selected first). al_x, al_y are screen pixels; for a PowerBuilder menu, PopMenu(PointerX(), PointerY()) of your window
ue_column_moved (string as_key, long al_from, long al_to)A column moved (ib_reorderable): its header dragged, Ctrl + Shift + Left / Right, or your code (of_move_column). al_from and al_to are its places among all the columns, hidden ones included, from 1. The column is already in place; ue_layout_changed follows
ue_column_moving (string as_key, long al_from, long al_to) → booleanCancelable, asked before a column moves, only when ib_veto_moves = true (the same opt-in as the cards): drag of the header, keyboard and of_move_column. Returning FALSE leaves the column where it is, and of_move_column returns -4
ue_column_rclicked (string as_key, long al_x, long al_y)A right click on the header of a column. Screen pixels, like ue_card_rclicked
ue_column_toggled (string as_key, boolean ab_collapsed)A column folded (ab_collapsed = TRUE) or opened: its chevron, a double click on its strip, the keyboard on its header (Enter or Space fold or open it, + opens it, - folds it), or its ib_collapsed set by your code
ue_delete_requested (string as_key)Delete was pressed on the selected card. The board removes nothing: of_remove_card once your application agrees. Never on a read-only card or board
ue_layout_changed (string as_layout_json)The layout changed: a column moved or folded, by the user or by your code, or of_set_layout. as_layout_json is the whole layout, as of_get_layout gives it: store it
ue_new_requested (string as_column, string as_title)The user asks for a new card in a column: its + Add a card button, the Insert key or a double click on its empty part; as_title is the title typed in the quick-add field (ib_quick_add), empty otherwise. The board adds nothing: of_add_card, after your own dialog if you like
ue_selection_changed (string as_from_key, string as_key)The selected card changed: a click, the keyboard, your code (of_select_card, of_clear_selection), or the selected card left the drawing (removed, hidden, folded, out of the search: as_key is then empty)
ue_write_back_failed (string as_key, string as_col, string as_value, string as_reason)ib_write_back: the DataStore refused the new column of a moved card (a value its column cannot hold, a row deleted since the load). A rank refused too (as_reason then starts with « the rank of row »). Nothing stays written: what was is put back. The card did not move (the DataStore is written before the move), ue_card_moved is not raised and of_move_card returns -4
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 hard limit and a rule of passage #

// Once, after the board is built : ask before each move
uo_board.ib_veto_moves = true

// event ue_card_moving of uo_board : a card reaches Done only from Review
if as_to_column = "done" and as_from_column <> "review" then return false

// and a column past its limit takes no more card (what its header counts :
// the visible cards)
if as_to_column <> as_from_column then
	if this.of_column(/*key*/ as_to_column).il_limit > 0 and this.of_column(/*key*/ as_to_column).of_visible_count() >= this.of_column(/*key*/ as_to_column).il_limit then return false
end if
return true

Finding the columns again at the next start #

// Once, after the board is built : the user may move the columns
uo_board.ib_reorderable = true

// The layout stored at the last start, if any
uo_board.of_set_layout(/*layout_json*/ ProfileString(gs_ini, "board", "layout", ""))

// event ue_layout_changed of uo_board : store every change
SetProfileString(gs_ini, "board", "layout", as_layout_json)

Opening a card in your window #

// event ue_card_opened of uo_board : the row of the card, edited in your window
OpenWithParm(w_task_edit, uo_board.of_card(/*key*/ as_key).of_row())

// The row may have changed : the card follows it
uo_board.of_reload_row(/*row*/ uo_board.of_card(/*key*/ as_key).of_row())

A team board: points, shared width, quick add #

// The estimate of a card, a field of our own
uo_board.of_set_field(/*key*/ "t1", /*field*/ "points", /*value*/ "5")

// Every header : its cards, its limit, the sum of the points of its cards
uo_board.is_count_template = "{count}{?limit}/{limit}{/limit} - {sum:points} pts"

// The open columns share the width of the board
uo_board.ii_column_width = 0

// + Add a card opens a title field at the foot of the column
uo_board.ib_add_button = true
uo_board.ib_quick_add = true

// event ue_new_requested of uo_board : the card typed, with a key of ours
uo_board.of_add_card(/*key*/ "new" + String(Cpu()), /*column*/ as_column, /*title*/ as_title)

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