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_resizedandue_progress_changedtell 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 #
| Userobject | u_pbt_gantt |
| Item class | n_pbt_gantt_task (of_task(keys)) |
| Used for | Planning and following a project: phases, tasks, milestones, links, delays |
| Principle | A task is named by its address ("build/design"); a summary computes its dates and progress from what it holds |
| Opt-in options | ib_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) + " %")
Links and critical path #
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 #
| Property | Type | Default | Purpose |
|---|---|---|---|
is_scale | string | "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_today | string | "" | 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_days | string | "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_columns | string | "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_format | string | "" | 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_template | string | "{label}" | The text beside each bar, written by a template (see above); empty: no text |
is_tooltip_title_template | string | "{label}" | The title of the tooltip of a bar, written by a template; empty with an empty is_tooltip_template: no tooltip |
is_tooltip_template | string | "{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_toolbar | boolean | true | The toolbar above the chart: the Today button and the four scales |
ib_read_only | boolean | false | The 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_changes | boolean | false | Opt-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_back | boolean | false | A 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_mouse | boolean | false | Two dots on each side of the bar under the pointer: dragging one onto another task asks for a link (ue_link_requested) |
ib_critical_path | boolean | false | Draws 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_end | string | "" | 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_late | boolean | false | Draws 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_width | integer | -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_height | integer | 30 | The height of a row in pixels, from 22 to 60; another value is refused (of_get_last_error) |
is_theme_style | string | "" | Visual style of the component (THEME_STYLE_* constants); empty = the application's, followed at every change |
is_theme_mode | string | "" | Light or dark variant (THEME_MODE_* constants); empty = the application's, followed at every change |
il_theme_accent | long | -1 | Accent colour of this component (-1 = the application accent, or the theme's) |
is_tooltip | string | "" | Simple tooltip shown when hovering the component |
is_super_tooltip_title | string | "" | Title of the rich tooltip (takes precedence over is_tooltip) |
is_super_tooltip_text | string | "" | Text of the rich tooltip (rich markup accepted) |
is_super_tooltip_image | string | "" | Image of the rich tooltip |
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.
| Property | Type | Default | Purpose |
|---|---|---|---|
is_label | string | "" | 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_start | string | — | 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_end | string | — | 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_progress | double | 0 | The 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_milestone | boolean | false | true: 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_collapsed | boolean | false | true 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_only | boolean | false | The user can neither drag this task, nor resize it, nor change its progress, nor ask to delete it; your code still changes it |
il_accent | long | -1 | The 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_color | long | -1 | The 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_color | long | -1 | The text colour of the task: in the grid and beside its bar. -1 = the component's |
il_back_color_hover | long | -1 | The background of the row of the task in the grid while the pointer is over it. -1 = the component's |
il_text_color_hover | long | -1 | The 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_tooltip | string | "" | Simple tooltip shown when hovering the item |
is_super_tooltip_title | string | "" | Title of the item rich tooltip (takes precedence over is_tooltip) |
is_super_tooltip_text | string | "" | Text of the item rich tooltip (rich markup accepted) |
is_super_tooltip_image | string | "" | Image of the item rich tooltip |
Constants #
| Constant | Value | Meaning |
|---|---|---|
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 #
| Method | Purpose |
|---|---|
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 #
| Event | Raised 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) → boolean | Cancelable, 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 #
- Dragging a bar moves it by whole days, its end edge changes its end, the small triangle under it its progress; a bubble shows the dates or the percentage, Escape cancels.
- Up / Down: the task above, below; Home / End: the first one, the last one; Page Up / Page Down: a page higher, lower. The view follows the selected task.
- Right opens a folded summary, then goes down to its first task; Left folds an open summary, then goes up to its parent. Read right to left, Left and Right swap.
- Ctrl + Left / Right moves the task by a day, Ctrl + Shift + Left / Right changes its end by a day.
- Enter or a double click opens the task, Delete asks to delete it, a right click or the Menu key (Shift + F10):
ue_task_rclicked. A double click on the row of a summary folds or opens it. - Ctrl + wheel changes the scale, the day under the pointer staying in place; the wheel over the grid scrolls the chart.
- Dragging the line between grid and chart changes the width of the grid, dragging the edge of a header that of its column.
- The chart is one Tab stop, the line between grid and chart a second one: Left / Right move it by 10 px (Shift: 50), Home hides the grid.
- Alt + Left / Right scrolls the time, the selection stays; a click in the empty part of a line of the chart selects its task.
- Near an edge, during a drag, the chart scrolls by itself: along the time for a bar, up or down for a link.
- The toolbar is one Tab stop: Left / Right go from button to button, Enter or Space presses it. In the chart, Ctrl + Plus / Minus changes the scale, Ctrl + Home brings today into view, Ctrl + Up / Down changes the progress of the task by 10 points.
- While a link is drawn, the bar it would reach is ringed, a thick edge on the side it would hold; a link already there or one that would close a loop is ringed in grey and is not asked for.
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
Links drawn with the mouse #
// 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 #
- Phases as summaries: two or three levels are enough, the user folds what he does not follow.
- Links rather than copied dates: a link the dates break shows, a copied date goes wrong silently.
- The critical path switched on when a date must be kept: it shows where a day late costs the project a day.
- Save in
ue_task_moved(or letib_write_backwrite the DataStore): the chart shows the new schedule before the database knows it. - Give the layout back (
ue_layout_changed,of_set_layout): the scale and widths the user chose are his.
Inherited from the common base #
These members exist on every visual component — they are not specific to this one. They are detailed once, in the transverse chapters; this table only says where to read them.
| Members | Role | Detailed in |
|---|---|---|
of_count · of_keys_at · of_has | Walk what the component holds | 3.2 Items |
of_reset | Put the component back to zero | 3.6 Resetting a component: of_reset() |
of_register_shortcut · of_clear_shortcuts | The component's keyboard chords | 3.5 Keyboard shortcuts |
of_is_created · of_is_ready · of_get_last_error | Whether it was born, whether it is ready, what failed | 3.7 Diagnostics |
of_save_as_png · of_save_as_jpg | Export the rendering as an image | 3.8 Exporting the rendering as an image |
of_set_redraw | Group changes into a single repaint | 3.10 Best practices |
of_preload_icons | Icons shown with no delay | Instant display: of_icon |
of_set_translation | Translate one of the component's labels | 5.2 Adapting a label: of_set_translation |
of_focus_webview | Give the component the focus | 6.4 Keyboard and focus |
of_print · of_print_to_pdf | Print, or write a PDF | 6.9 Printing |
of_set_property · of_get_property · of_component_name | Driving a property by its name | 3.1 The property engine |
is_accessible_name · is_accessible_description | The name and description a screen reader says | 3.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.