PBToolboxAI v5 ← Site

gantt — u_pbt_gantt #

← Component reference · Guide contents

gantt plans a project: on the left a grid of tasks (name, start, finish, duration, progress), on the right their bars on a time scale — day, week, month or quarter — with the line of today (in the accent colour) and the days off shaded. A task that holds others is a summary whose dates and progress are computed; a milestone is a one-day diamond; an arrow links a task to the one that waits for it, and the critical path is drawn in red. The user drags a bar to move it, its end edge to change its end, the small triangle under it for its progress; ue_task_moved, ue_task_resized and ue_progress_changed tell you. The plan is filled by hand or from a DataStore, which receives the changes.

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


At a glance #

Userobjectu_pbt_gantt
Item classn_pbt_gantt_task (of_task(keys))
Used forPlanning and following a project: phases, tasks, milestones, links, delays
PrincipleA task is named by its address ("build/design"); a summary computes its dates and progress from what it holds
Opt-in optionsib_veto_changes, ib_write_back, ib_link_by_mouse, ib_critical_path, ib_show_late, ib_read_only

Quick start #

// A phase : the task that holds others becomes a summary
uo_plan.of_add_task(/*keys*/ "build", /*label*/ "Build", /*start*/ "2026-11-02", /*end*/ "2026-11-20")
uo_plan.of_add_task(/*keys*/ "build/design", /*label*/ "Design", /*start*/ "2026-11-02", /*end*/ "2026-11-06")
uo_plan.of_add_task(/*keys*/ "build/code", /*label*/ "Code", /*start*/ "2026-11-09", /*end*/ "2026-11-20")

// A milestone : an instant at the end of its day, here the day the code ends
uo_plan.of_add_milestone(/*keys*/ "golive", /*label*/ "Go-live", /*date*/ "2026-11-20")

// The links : a task starts once the one before it is finished
uo_plan.of_add_dependency(/*from_keys*/ "build/design", /*to_keys*/ "build/code")
uo_plan.of_add_dependency(/*from_keys*/ "build/code", /*to_keys*/ "golive")

// What is done already
uo_plan.of_task(/*keys*/ "build/design").id_progress = 100
// event ue_task_moved of uo_plan : (string as_keys, string as_start, string as_end, string as_old_start, string as_old_end)
// Local variables
string ls_code

// The code of the task : the LAST level of its address ("build/design" -> "design")
ls_code = this.of_task(/*keys*/ as_keys).of_key()

// Save the new dates of the task
UPDATE tasks SET start_date = :as_start, end_date = :as_end WHERE code = :ls_code;

Tasks, summaries and milestones #

A task is named by its address: its key under its parent, the levels joined by / — "build" at the top, "build/design" inside the task "build", added first. A task that holds others becomes a summary: a thin bar, its dates run from the first start to the last day of what it holds, its progress is their average weighted by the durations. Neither the user nor your code writes its dates or its progress: they are computed. Its chevron, a double click on its row or ib_collapsed fold it; ue_task_toggled follows.

Dates are whole days, yyyy-mm-dd, the end included: a task from the 6th to the 10th lasts five days, counted on the calendar. of_add_task and of_add_milestone also take two PowerBuilder date values. A milestone has no length: one day, a diamond, no progress. Through the handle of a task (of_task(address)), writing is_start moves it (its length stays), is_end changes its end, id_progress its progress; a value written by your code raises nothing, like SetItem. The same handle reads what the task is now: its dates, its length (of_duration), its place among its siblings counted from 1 (of_position), its level (of_level).

// The test starts three days later : it moves, its length stays
uo_plan.of_task(/*keys*/ "test").is_start = "2026-11-26"

// Read back what the summary computed
MessageBox("Build", uo_plan.of_task(/*keys*/ "build").is_end + " - " + String(uo_plan.of_task(/*keys*/ "build").id_progress) + " %")

of_add_dependency links two tasks with an arrow. The default type is finish-to-start (DEP_FS: the next one starts once the previous one is finished); DEP_SS (start-to-start), DEP_FF (finish-to-finish) and DEP_SF (start-to-finish) are given as a third argument. A link the dates break — the next one starts too early — is drawn dashed in the danger colour. A link to itself, between a task and what it holds, a link already there or a link that would close a loop is refused (-5). of_dependency_type reads the type of a link, of_predecessors and of_successors of the handle the linked tasks.

With ib_critical_path = true, the critical path is drawn in the danger colour: the tasks and links without float, the ones that push the end of the project if they slip by a day. of_is_critical of the handle reads it, whether the path is drawn or not. With ib_link_by_mouse = true, two dots appear on each side of the bar under the pointer: the user drags one onto another task, and ue_link_requested gives the link asked for — its type follows the dot it started from and the half of the target bar. The chart adds nothing by itself: of_add_dependency once your application agrees. A click on an arrow raises ue_dependency_clicked. The float is counted in working days (is_work_days): a task that ends on a Friday followed by one that starts on the Monday has none. A milestone is an instant at the end of its day: set on the day the task it follows ends, the link holds, and the task after it starts the next day. A link that touches a summary binds the tasks it holds; a summary is never marked, and an arrow is red only between two red bars. A finished task (100 %) is never marked: it drives nothing any more. is_project_end counts the float up to a deadline instead of the last finish.


Calendar: holidays, lags, markers and delays #

A project follows a calendar. is_work_days gives the working days of the week; of_add_holiday adds a public holiday, one by one — shaded like a week-end, named in the tooltip of its day at the day scale, it counts neither in the float of the critical path nor in a lag. A link may carry a lag in working days: of_add_dependency("design", "build", DEP_FS, 2) leaves two working days between the end of one and the start of the other (a drying time, a delivery); negative, it is a lead. The rule of the arrow and the critical path count it, of_dependency_lag reads it back, a DataStore writes it after the type ("12:fs+2"). of_add_marker draws a vertical marker at the end of a day — a contractual deadline, the end of a sprint — with its label at the top. Last, with ib_show_late = true, a late task — its last day is past (is_today) and it is not finished — is drawn in amber, hatched; of_is_late() of its handle and the {late} field of the templates say it either way.

// The public holidays of the year : days off, never float
uo_plan.of_add_holiday(/*date*/ "2026-11-11", /*label*/ "Armistice Day")
uo_plan.of_add_holiday(/*date*/ "2026-12-25", /*label*/ "Christmas")

// The paint dries two working days before the second coat
uo_plan.of_add_dependency(/*from_keys*/ "paint", /*to_keys*/ "second_coat", /*type*/ u_pbt_gantt.DEP_FS, /*lag*/ 2)

// The contractual deadline, in red
uo_plan.of_add_marker(/*key*/ "deadline", /*date*/ "2026-12-18", /*label*/ "Contract", /*color*/ RGB(/*red*/ 196, /*green*/ 43, /*blue*/ 28))

// The tasks behind schedule, in amber
uo_plan.ib_show_late = true

What the user changes #

The user drags a bar to move the task, by whole days (ue_task_moved), its end edge to change its end (ue_task_resized), the small triangle under the bar for its progress (ue_progress_changed); a bubble shows the dates or the percentage while dragging, Escape cancels. With the keyboard, Ctrl + Left / Right moves the selected task by a day and Ctrl + Shift + Left / Right changes its end. The events arrive with the task already in place. Only a gesture raises them: a date written by your code is silent, like SetItem. A summary cannot be dragged, nor can an ib_read_only task or chart.

With ib_veto_changes = true, ue_task_changing is asked before each of these three changes, with the dates and the progress the task would take: returning FALSE leaves it as it was. Delete on the selected task raises ue_delete_requested: the chart removes nothing, of_remove_task once your application agrees.


Scale, grid and layout #

The scale (is_scale) is chosen in the toolbar (ib_toolbar, with the Today button), with Ctrl + wheel — the day under the pointer stays under the pointer — or by your code; ue_scale_changed follows. At the day and week scales, the days off (is_work_days) are shaded. The grid shows the columns of is_columns, in their order, its dates written by is_date_format; the user drags the line between grid and chart (ii_grid_width) and the edge of a column header for its width. What he arranged — the scale, the widths, the folded summaries — 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.


The text of the bars and tooltips #

The text beside each bar is written by a template, is_bar_template ("{label}" by default, empty for none), and the tooltip of a bar by is_tooltip_title_template and is_tooltip_template: rich text markup where {owner} is a field of the task — one of yours (of_set_field), a column of the DataStore it came from, or a built-in field (label, key, keys, start, end, duration, days, progress, percent, milestone, summary, critical, late, level, parent). {end:d mmm} writes a date with the tokens d dd ddd dddd m mm mmm mmmm yy yyyy, {?owner}…{/owner} keeps a piece when the field says something, {!owner}…{/owner} when it says nothing, {x:raw} keeps a value as markup, {{ }} are braces. {label} and {parent} are markup, as in the grid; any other value is shown as it is, never read as markup. A built-in field name is never yours: of_set_field refuses it, and a DataStore column named like it is hidden by it. A task with a tooltip of its own (is_tooltip of its handle) shows that one instead.

// Who does what : a field of our own on a task
uo_plan.of_set_field(/*keys*/ "build/design", /*field*/ "owner", /*value*/ "Ada")

// Beside each bar : the label, the owner when there is one, the end day
uo_plan.is_bar_template = "{label}{?owner} - [b]{owner}[/b]{/owner} [symbol=calendar] {end:d mmm}"

From a DataStore #

of_from_datastore makes one task of each row. Its key is the value of the ROLE_KEY column — an id — or, without it, the RowID of the row; the ROLE_PARENT column names the key of the task that holds it (empty: at the top; a parent not found or a loop puts the row at the top). A column plays a role when it is named like it (key, parent, label, start, end, progress, milestone, color, predecessors) or when of_map says so. ROLE_MILESTONE makes a milestone of any value that says yes; ROLE_COLOR colours the bar (a PowerBuilder RGB() or #rrggbb); ROLE_PREDECESSORS lists the tasks waited for, their keys separated by commas, the type after : when it is not finish-to-start: "12, 14:ss". Dates are read in the regional order of the workstation. Every column is a field of the templates. The tasks of an earlier load leave (their handles are freed), those added by hand stay. Two rows with one key: the second becomes key~RowID; a key holding a / or a | takes the RowID, and its children still find it by its value; a key a task added by hand already holds becomes key~RowID, under the same parent — no row is ever left out. A row whose start is empty is a task not scheduled: the grid says it, it has no bar and no gesture moves it. Dates and decimals are read whatever the regional settings (12,5 is 12.5). of_from_datastore also takes a DataWindow or a DataWindowChild: the rows the user sees beside the chart, written back there. After a Reset or a Retrieve of the DataStore, give it again: its RowIDs start over.

With ib_write_back = true, a gesture writes into the row before its event: the start and end dates for a move or a new end, the progress for the triangle — each value in the type of its column. The event reads the DataStore up to date, the Update stays yours. A value the DataStore refuses puts the task back as it was, and what was already written too, and raises ue_write_back_failed instead of the event of the gesture. After changing a row yourself, of_reload_row sends it to the chart again; a row inserted since arrives too. A task you remove (of_remove_task) stays out until the next of_from_datastore: of_map and the reload of another row do not bring it back, of_reload_row of its own row does. After of_clear, of_reload_row refuses (-5) until a new load.

// Which column of the DataStore plays which role
uo_plan.of_map(/*role*/ u_pbt_gantt.ROLE_KEY, /*column*/ "id")
uo_plan.of_map(/*role*/ u_pbt_gantt.ROLE_PARENT, /*column*/ "parent_id")
uo_plan.of_map(/*role*/ u_pbt_gantt.ROLE_START, /*column*/ "start_date")
uo_plan.of_map(/*role*/ u_pbt_gantt.ROLE_END, /*column*/ "end_date")
uo_plan.of_map(/*role*/ u_pbt_gantt.ROLE_PREDECESSORS, /*column*/ "after")

// A change goes into the DataStore, then the rows become tasks
uo_plan.ib_write_back = true
uo_plan.of_from_datastore(/*ads*/ ids_tasks)

Properties #

PropertyTypeDefaultPurpose
is_scalestring"week"The time scale: SCALE_DAY, SCALE_WEEK, SCALE_MONTH, SCALE_QUARTER. An unknown value is refused: the old one stays, of_get_last_error says why. The toolbar and Ctrl + wheel change it too: read back live
is_todaystring""The day of the today line, in the accent colour, 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_work_daysstring"1|2|3|4|5"The working days, ISO numbers joined by | (1 = Monday … 7 = Sunday, 0 accepted for Sunday); the others are shaded at the day and week scales, and the float of the critical path counts only these. They are not the numbers of DayNumber(), where 1 = Sunday. A list that names no day is refused (of_get_last_error)
is_columnsstring"name|start|end|duration|progress"The columns of the grid, in their order, joined by |: COLUMN_NAME, COLUMN_START, COLUMN_END, COLUMN_DURATION, COLUMN_PROGRESS. An unknown column, or none, is refused: the old list stays, of_get_last_error says why
is_date_formatstring""How the grid writes a date, with the tokens d dd ddd dddd m mm mmm mmmm yy yyyy; empty: the short date of the display language
is_bar_templatestring"{label}"The text beside each bar, written by a template (see above); empty: no text
is_tooltip_title_templatestring"{label}"The title of the tooltip of a bar, written by a template; empty with an empty is_tooltip_template: no tooltip
is_tooltip_templatestring"{start} – {end} ({duration}){!milestone}{!summary}[br]{percent}{/summary}{/milestone}"The text of the tooltip of a bar, written by a template: by default the dates, the duration and, except for a milestone or a summary, the progress; {percent} writes the progress as a percentage, the way the display language writes it (50% in English)
ib_toolbarbooleantrueThe toolbar above the chart: the Today button and the four scales
ib_read_onlybooleanfalseThe user changes nothing: no drag, no resize, no progress, no Ctrl + arrows, no Delete, no link. He still selects, folds, opens and zooms; your code still changes the tasks
ib_veto_changesbooleanfalseOpt-in: ask ue_task_changing before every move, new end or progress of the user. Off by default, like every cancelable event of the library
ib_write_backbooleanfalseA chart fed by of_from_datastore: a gesture writes the new dates or the new progress into the DataStore before its event. A task added by hand is never written. A datetime column keeps its time: the chart moves days. A task of a DataStore without an end column (ROLE_END) has no end grip: its new end could not be written
ib_link_by_mousebooleanfalseTwo dots on each side of the bar under the pointer: dragging one onto another task asks for a link (ue_link_requested)
ib_critical_pathbooleanfalseDraws the critical path in the danger colour: the tasks and links without float, counted in working days (is_work_days); a link that touches a summary binds the tasks it holds; a finished task (100 %) is never marked
is_project_endstring""The deadline of the project, yyyy-mm-dd: the float of the critical path is counted up to the end of that day instead of the last finish of the plan. Later, every task has play; earlier, the tasks that end too late are critical. Empty: the last finish. A date that is not one is refused (of_get_last_error)
ib_show_latebooleanfalseDraws the late tasks in amber, hatched: a task — not a summary, not a milestone — whose last day is before is_today and whose progress is under 100. of_is_late() and the {late} field answer whether it is drawn or not
ii_grid_widthinteger-1 (auto)The width of the grid in pixels. Automatic by default (-1): as wide as the columns it shows, and read back as that width (544 with the five columns). 0 hides it, 2000 at most; another value is refused (of_get_last_error). The user drags the line between grid and chart, or moves it with the arrows: read back live
ii_row_heightinteger30The height of a row in pixels, from 22 to 60; another value is refused (of_get_last_error)
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 task — n_pbt_gantt_task #

Its handle, through of_task(address). A read asks the chart what the task is now; a value written is silent, like SetItem; ib_collapsed is an order and raises ue_task_toggled. Its of_count() counts the tasks it holds and its of_keys_at(n) returns the address of the n-th.

PropertyTypeDefaultPurpose
is_labelstring""The name of the task, in the grid, beside its bar and in its tooltip: rich text markup everywhere ([[1]] for a bracket). Empty, the task shows its key
is_startstring—The first day, yyyy-mm-dd. Writing it moves the task: its length stays (write is_end after it to change the length). Refused for a summary, which takes its dates from what it holds, and for a date that is not one: of_get_last_error says why
is_endstring—The last day, included, yyyy-mm-dd: a task from the 6th to the 10th lasts five days. Refused before is_start, for a milestone (it has no end of its own) and for a summary
id_progressdouble0The progress, in percent (0 to 100), drawn inside the bar. A summary reads the average of what it holds, weighted by the durations; writing it is refused, for a milestone too
ib_milestonebooleanfalsetrue: a milestone, one day drawn as a diamond, no length, no progress. It is an instant at the end of its day: linked after a task that ends that day, the rule holds; a task linked after it starts the next day
ib_collapsedbooleanfalsetrue folds a summary: the tasks it holds leave the grid. ue_task_toggled follows, as for a click on its chevron. Only a summary folds: true on a task that holds nothing is refused (of_get_last_error). Read back live
ib_read_onlybooleanfalseThe user can neither drag this task, nor resize it, nor change its progress, nor ask to delete it; your code still changes it
il_accentlong-1The colour of the bar and its progress, as RGB(r,g,b); it wins over a ROLE_COLOR column. -1 = the accent of the component. A task on the drawn critical path keeps the danger colour
il_back_colorlong-1The background of the task: its row in the grid and, as a pale wash, the line of its bar in the chart, as RGB(r,g,b). -1 = the component's
il_text_colorlong-1The text colour of the task: in the grid and beside its bar. -1 = the component's
il_back_color_hoverlong-1The background of the row of the task in the grid while the pointer is over it. -1 = the component's
il_text_color_hoverlong-1The text colour of the row of the task in the grid while the pointer is over it. -1 = the component's
of_duration ( ) → long——Returns its length in calendar days, the last day included; 0 for a milestone
of_is_summary ( ) → boolean——TRUE when the task holds others: its dates and progress are computed
of_is_critical ( ) → boolean——TRUE when the task is on the critical path: no float, a day late and the project ends a day later. Answered whether ib_critical_path draws it or not. The float is counted in working days (is_work_days); a summary is never critical, the tasks it holds may be; a finished task (100 %) never is
of_position ( ) → long——Returns its place among the tasks of its parent, counted from 1; 0 when the chart does not hold it
of_level ( ) → long——Returns its depth: 1 for a task at the top, 2 for a task inside it…
of_predecessors ( ) → string——The addresses of the tasks it depends on, joined by |; empty when none
of_successors ( ) → string——The addresses of the tasks that depend on it, joined by |; empty when none
of_is_late ( ) → boolean——Late: its last day is before is_today and it is not finished (progress under 100). A summary and a milestone never are; answers whether ib_show_late draws it or not
of_row ( ) → long——Returns the row number today, in the DataStore of of_from_datastore, of the row the task comes from — whatever key it had to take, and after a sort or a filter. 0 for a task 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

Constants #

ConstantValueMeaning
SCALE_DAY"day"One column per day
SCALE_WEEK"week"One column per week — the default scale
SCALE_MONTH"month"One column per month
SCALE_QUARTER"quarter"One column per quarter
COLUMN_NAME"name"The name of the task, as a tree
COLUMN_START"start"The first day
COLUMN_END"end"The last day
COLUMN_DURATION"duration"The length in days
COLUMN_PROGRESS"progress"The progress in percent
DEP_FS"fs"Finish-to-start: the next one starts once the previous one is finished (the usual link)
DEP_SS"ss"Start-to-start: they start together
DEP_FF"ff"Finish-to-finish: they finish together
DEP_SF"sf"Start-to-finish: the next one finishes when the previous one starts
CHANGE_MOVE"move"A move: both dates
CHANGE_RESIZE"resize"A new end
CHANGE_PROGRESS"progress"A new progress
ROLE_KEY"key"The key of the task (an id); without it, the RowID
ROLE_PARENT"parent"The key of the task that holds it
ROLE_LABEL"label"Its name
ROLE_START"start"Its first day
ROLE_END"end"Its last day (empty: the first one)
ROLE_PROGRESS"progress"Its progress, from 0 to 100
ROLE_MILESTONE"milestone"A milestone when the value says yes
ROLE_COLOR"color"The colour of its bar
ROLE_PREDECESSORS"predecessors"The tasks it waits for: "12, 14:ss, 15:fs+2" (a lag in working days is written after the type)

Methods #

MethodPurpose
of_add_dependency (string as_from_keys, string as_to_keys)Adds a finish-to-start link: as_to_keys starts once as_from_keys is finished. Returns 0 once added, -5 or -2 like the three-argument form
of_add_dependency (string as_from_keys, string as_to_keys, string as_type)Adds a link of a type DEP_FS, DEP_SS, DEP_FF or DEP_SF, drawn as an arrow. Returns 0 once added, -5 for an unknown task, a link to itself or between a task and what it holds, a link already there (whatever its type), a link that would close a loop or an unknown type, -2 when the component is not created
of_add_dependency (string as_from_keys, string as_to_keys, string as_type, long al_lag)The same link with a lag in working days (is_work_days, less the holidays): 2 leaves two working days between the end of one and the start of the other, a negative number is a lead. The rule of the arrow and the critical path count it. Returns 0 once added, -5 like the three-argument form or for a lag beyond 999 days, -2 when the component is not created
of_add_holiday (string as_date)Adds a holiday without a name. Returns 0 once added, -5 or -2 like the two-argument form
of_add_holiday (string as_date, string as_label)Adds a public holiday, yyyy-mm-dd: a day off whatever is_work_days says, shaded like a week-end, named by as_label in the tooltip of its day at the day scale; the float of the critical path and the lag of a link do not count it. One by one; of_clear keeps it, of_reset clears it. Returns 0 once added, -5 for a date that is not one or a day already a holiday, -2 when the component is not created
of_add_holiday (date ad_date, string as_label)The same from a PowerBuilder date. Returns 0 once added, -5 also for a null date
of_add_marker (string as_key, string as_date, string as_label)Adds a vertical marker in the muted text colour. Returns 0 once added, -5 or -2 like the four-argument form
of_add_marker (string as_key, string as_date, string as_label, long al_color)Adds a vertical marker: a dashed line at the end of its day (a deadline holds to the end of its day, like a milestone), its label — rich text markup — at the top of the chart: a contractual deadline, the end of a sprint, a freeze. al_color is an RGB() colour, -1 for the muted text colour. Returns 0 once added, -5 for a key that cannot be one (empty, holding / or |), a key already taken or a date that is not one, -2 when the component is not created
of_add_milestone (string as_keys, string as_label, string as_date)Adds a milestone at its address: one day, a diamond, no length. Returns 0 once added, -5 like of_add_task, -2 when the component is not created
of_add_milestone (string as_keys, string as_label, date ad_date)The same from a PowerBuilder date. Returns 0 once added, -5 also for a null date
of_add_task (string as_keys, string as_label, string as_start, string as_end)Adds a task at its address: "phase1" at the top, "phase1/design" inside the task "phase1", added first (it becomes a summary). Dates yyyy-mm-dd, the end included ("": one day). Returns 0 once added, -5 for an address with an empty level or a key holding |, an address already taken, an unknown parent, a date that is not one or an end before the start, -2 when the component is not created
of_add_task (string as_keys, string as_label, date ad_start, date ad_end)The same from two PowerBuilder date values, the end being the last day, included. Returns 0 once added, -5 also for a null start (a null end: one day)
of_clear ( )Empties the chart: tasks, links, their handles and the rows of the DataStore (it stays bound, for a new of_from_datastore; of_reload_row refuses until then); the options stay. Returns 0 once applied, -2 when the component is not created
of_clear_dependencies ( )Removes every link; the tasks stay. Returns 0 once applied, -2 when the component is not created
of_clear_holidays ( )Removes every holiday: only is_work_days says which days work. Returns 0 once applied, -2 when the component is not created
of_clear_markers ( )Removes every marker; of_clear keeps them, like the holidays. Returns 0 once applied, -2 when the component is not created
of_clear_selection ( )Leaves no task selected: ue_selection_changed follows with an empty address; nothing selected, nothing is raised. Returns 0 once applied, -2 when the component is not created
of_collapse_all ( )Folds every summary: ue_task_toggled follows for each one that was open, then ue_layout_changed once. Returns 0 once applied, -2 when the component is not created
of_dependency_lag (string as_from_keys, string as_to_keys)Returns the lag of the link from a task to another, in working days, read live: negative for a lead, 0 without a lag or without a link
of_dependency_type (string as_from_keys, string as_to_keys)The type of the link from a task to another (DEP_FS…), empty when there is none. Read live
of_expand_all ( )Opens every summary: ue_task_toggled follows for each one that was folded, then ue_layout_changed once. Returns 0 once applied, -2 when the component is not created
of_from_datastore (datastore ads)Makes a task of each row of the DataStore (see above). The tasks of an earlier load leave, those added by hand stay, their handles too. 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. A DataWindow that cannot share its data is refused. Returns 0 once loaded, -5 when the DataWindow is not valid, has no column or cannot share, -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, -2 when the component is not created
of_get_field (string as_keys, string as_field)Reads a field of a task, live: one of yours, a column of its DataStore or a built-in field (duration, critical…) — which gives the text shown and hides a column of the same name. An empty string when it has none
of_get_last_error ( )Why the last write of a property was refused (a date that is not one, the dates of a summary, an unknown scale or column); otherwise the last error of the library
of_get_layout ( )What the user arranged, as JSON: {"scale":"week","gridWidth":-1,"columns":[{"key":"name","width":200},...],"collapsed":["phase2"]} — the scale, the width of the grid and of each column, the folded summaries. gridWidth is -1 while the grid is automatic. To store, then give back to of_set_layout. Read live
of_holidays ( )The holidays, read live: yyyy-mm-dd joined by |, in date order; empty when none
of_map (string as_role, string as_column)Gives a column of the DataStore a role (ROLE_KEY, ROLE_PARENT, ROLE_LABEL, ROLE_START, ROLE_END, ROLE_PROGRESS, ROLE_MILESTONE, ROLE_COLOR, ROLE_PREDECESSORS), before or after of_from_datastore: after, the rows are read again. An empty column gives the role back to the column named like it. What was set on the tasks stays (fold, read-only, fields, colours, links added by the code). A new ROLE_KEY or ROLE_PARENT gives the tasks of the rows new addresses: their handles are freed, those of the tasks added by hand stay. 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_markers ( )The keys of the markers, read live, in their order, joined by |; empty when none
of_reload_row (long al_row)Sends a row of the DataStore of of_from_datastore to the chart again, read now, after your code changed it (SetItem, an edit window); al_row is its row number today. The task follows its row in place: what was set on it (fold, read-only, fields, colours, links added by the code) stays, a drag under way goes on, nothing is raised. A row whose key or parent changed moves its task, with what was set on it; a row inserted since arrives too. Returns 0 once sent, -5 when no DataStore is bound, the chart holds no rows since of_clear or the row does not exist (of_get_last_error says which), -2 when the component is not created
of_remove_dependency (string as_from_keys, string as_to_keys)Removes the link from a task to another. Returns 0 once removed, -5 for a link the chart does not hold, -2 when the component is not created
of_remove_holiday (string as_date)The day works again. Returns 0 once removed, -5 for a day that is not a holiday of this chart, -2 when the component is not created
of_remove_marker (string as_key)Removes a marker. Returns 0 once removed, -5 for a key this chart does not hold, -2 when the component is not created
of_remove_task (string as_keys)Removes a task, everything it holds and every link that touches them, and frees their handles; removing the selected task clears the selection. The task of a DataStore row stays out until the next of_from_datastore; of_reload_row of its row brings it back. Returns 0 once removed, -5 for an address the chart does not hold, -2 when the component is not created
of_reset ( )Empties the chart, forgets the DataStore and puts every property back to its default, every column to its width. Returns 0 once applied, -2 when the component is not created
of_scroll_to_date (string as_date)Scrolls the time axis until that day (yyyy-mm-dd) is in sight, a third from the start of the view. Returns 0 once applied, -5 for a date that is not one, -2 when the component is not created
of_scroll_to_task (string as_keys)Shows a task: the summaries that hold it open (ue_task_toggled for each), then the chart scrolls until its row and its bar are in sight. Returns 0 once applied, -5 for a task the chart does not hold, -2 when the component is not created
of_select_task (string as_keys)Selects a task 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 task or one not drawn (inside a folded summary, beyond the demo cap), -2 when the component is not created
of_selected_key ( )The address of the selected task, whoever selected it; empty if none. Read live
of_set_field (string as_keys, string as_field, string as_value)Gives a task a field of your own, for the templates; 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 task, a name that is not one or the name of a built-in field (label, start…), -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 scale, the widths, and which summaries are folded (the ones it does not name open); a column or a task the chart does not hold is ignored. ue_layout_changed is raised once when something changed (neither ue_task_toggled nor ue_scale_changed: 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
of_task (string as_keys)Returns the handle of a task, by its address (created on first access, freed when the task is removed)

Events #

EventRaised when
ue_task_moved (string as_keys, string as_start, string as_end, string as_old_start, string as_old_end)The user moved a task: its bar dragged, or Ctrl + Left / Right. as_start and as_end are its new days, as_old_start and as_old_end the ones it had, yyyy-mm-dd — what a log or an undo needs. The task is already in place; with ib_write_back, the DataStore is already written. A date written by your code raises nothing
ue_task_resized (string as_keys, string as_start, string as_end, string as_old_start, string as_old_end)The user changed the end of a task: the end edge of its bar dragged, or Ctrl + Shift + Left / Right. The old days come with the new ones
ue_progress_changed (string as_keys, double ad_progress, double ad_old_progress)The user changed the progress of a task: the small triangle under its bar dragged, or Ctrl + Up / Down (10 points). ad_progress is the new one, in percent, ad_old_progress the one it had
ue_task_changing (string as_keys, string as_kind, string as_start, string as_end, double ad_progress) → booleanCancelable, asked before a move, a new end or a progress of the user, only when ib_veto_changes = true. as_kind says which: CHANGE_MOVE, CHANGE_RESIZE or CHANGE_PROGRESS; the values are the ones the task would take. Returning FALSE leaves it as it was
ue_task_clicked (string as_keys)A task was clicked (and selected): its row in the grid or its bar. Not at the end of a drag
ue_task_opened (string as_keys)A double click on a task, or Enter: open your editor. A double click on the row of a summary folds or opens it instead
ue_task_rclicked (string as_keys, long al_x, long al_y)A right click on a task (selected first), or the Menu key (Shift + F10) on the selected task. al_x, al_y are screen pixels; for a PowerBuilder menu, PopMenu(PointerX(), PointerY()) of your window
ue_selection_changed (string as_from_keys, string as_keys)The selected task changed: a click, the keyboard, your code (of_select_task, of_clear_selection), or the selected task left the grid (removed, inside a folded summary: as_keys is then empty)
ue_task_toggled (string as_keys, boolean ab_collapsed)A summary folded (ab_collapsed = TRUE) or opened: its chevron, a double click on its row, the keyboard, its ib_collapsed set by your code, of_expand_all, of_collapse_all, of_scroll_to_task
ue_delete_requested (string as_keys)Delete was pressed on the selected task. The chart removes nothing: of_remove_task once your application agrees. Never on a read-only task or chart
ue_link_requested (string as_from_keys, string as_to_keys, string as_type)The user drew a link from one task to another (ib_link_by_mouse); as_type is one of the DEP_ constants. The chart adds nothing: of_add_dependency once your application agrees
ue_dependency_clicked (string as_from_keys, string as_to_keys, string as_type)An arrow between two tasks was clicked
ue_scale_changed (string as_scale)The scale changed: the toolbar, Ctrl + wheel, or is_scale set by your code
ue_layout_changed (string as_layout_json)The layout changed: the scale, the width of the grid or of a column, a summary folded or opened (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_write_back_failed (string as_keys, string as_col, string as_value, string as_reason)ib_write_back: the DataStore refused a new value of a task (a type its column cannot hold, a row deleted since the load). The task went back as it was, and the event of the gesture is not raised
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 #

Planning rules #

// Once, after the plan is built : ask before each change of the user
uo_plan.ib_veto_changes = true

// event ue_task_changing of uo_plan : nothing ends after the go-live
if as_keys <> "golive" and as_end > this.of_task(/*keys*/ "golive").is_start then return false

// and nothing moves into the past : a task already started keeps its start
if as_start < String(Today(), "yyyy-mm-dd") and as_start < this.of_task(/*keys*/ as_keys).is_start then return false
return true
// Once : two dots beside the bar under the pointer, the critical path in red
uo_plan.ib_link_by_mouse = true
uo_plan.ib_critical_path = true

// event ue_link_requested of uo_plan : the chart adds nothing by itself
if this.of_add_dependency(/*from_keys*/ as_from_keys, /*to_keys*/ as_to_keys, /*type*/ as_type) = -5 then
	MessageBox("Plan", "This link cannot be added.")
end if

// event ue_dependency_clicked of uo_plan : a click on an arrow removes it
this.of_remove_dependency(/*from_keys*/ as_from_keys, /*to_keys*/ as_to_keys)

Finding the layout again at the next start #

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

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

Opening a task in your window #

// event ue_task_opened of uo_plan
// Local variables
long ll_row

// The row of the task in the DataStore, whatever key the task had to take
ll_row = this.of_task(/*keys*/ as_keys).of_row()

// A task added by hand has no row
if ll_row = 0 then return

// Edited in your window, then the task follows its row
OpenWithParm(w_task_edit, ll_row)
this.of_reload_row(/*row*/ ll_row)

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