YOUSEFUL / DELPHI / COMPONENTS
TAtlasTaskPlan
Task model with intervals, progress, acyclic finish-to-start dependencies, staged automatic scheduling and transactional JSON.
Description
TAtlasTaskPlan stores TAtlasTask records and their dependencies. Each task has a caption, a half-open TDateTime interval and a progress between 0 and 100. Dependencies are directed edges from a predecessor to a successor, kept in an internal TAtlasGraph; AddDependency rejects an edge that would close a cycle, a self edge or a duplicate, and leaves the plan unchanged. Task IDs are the graph's node IDs: sequential from 1, never reused.
AutoSchedule applies finish-to-start logic: every task starts at the project start or at the latest end of its predecessors, whichever is later, keeping its duration and progress. It is computed on a copy and swapped in only when every resulting task is valid. There is no working-day calendar, no critical path, no resource levelling and no other dependency type; when you need those, compute the dates in application code and call UpdateTask.
Views such as TAtlasGantt attach with AddListener; OnChange fires after them. Errors raise EAtlasGraphError, declared in the Atlas.Graph unit (add it to uses to catch it), including the Unknown task, Invalid task interval or progress and Graph contains a cycle messages. The Dependencies result uses TAtlasGraphEdge from the same unit, with SourceID the predecessor and TargetID the successor.
Members
| Member | Kind | What it does |
|---|---|---|
Create | constructor | Creates an empty plan with its own internal dependency graph. |
Destroy | destructor | Frees the tasks, the graph and the listener list; attached TAtlasGantt controls detach through the component free notification. |
AddTask | method | Adds a task and returns its ID. |
UpdateTask | method | Replaces caption, interval and progress of task ID with the same validation as AddTask; the caption is also written to the graph node. |
DeleteTask | method | Removes task ID together with every dependency that starts or ends at it. |
Task | method | Returns a copy of the task record with the given ID. |
Tasks | method | Copies of all tasks in creation order (the order of the graph nodes), which is also the row order of TAtlasGantt. |
Dependencies | method | All dependencies as TAtlasGraphEdge records (declared in Atlas.Graph): SourceID is the predecessor task and TargetID the successor. |
AddDependency | method | Adds a finish-to-start dependency: SuccessorID may start once PredecessorID has ended. |
DeleteDependency | method | Removes the dependency from PredecessorID to SuccessorID. |
AutoSchedule | method | Recomputes every task's dates in topological order: a task without predecessors starts at ProjectStart; a task with predecessors starts at the latest EndsAt among them (or ProjectStart if that is later). |
ToJSON | method | Serializes the plan as an object with two keys: graph, the internal TAtlasGraph JSON (nodes with id, caption, x, y, width and height, and edges with source and target), and tasks, an array of objects with id, caption, start, end (TDateTime numbers) and progress. |
LoadJSON | method | Replaces the plan with the content of a ToJSON string. |
AddListener | method | Registers a TNotifyEvent called with the plan as Sender after every change, before OnChange. |
RemoveListener | method | Removes a listener registered with AddListener. |
OnChange | event | Fires after every successful AddTask, UpdateTask, DeleteTask, AddDependency, DeleteDependency, AutoSchedule and LoadJSON, after the listeners. |
Constructors
Create constructor
constructor Create(AOwner: TComponent); override;Creates an empty plan with its own internal dependency graph.
Destructors
Destroy destructor
destructor Destroy; override;Frees the tasks, the graph and the listener list; attached TAtlasGantt controls detach through the component free notification.
Events
OnChange event
property OnChange: TNotifyEvent read FOnChange write FOnChange;Fires after every successful AddTask, UpdateTask, DeleteTask, AddDependency, DeleteDependency, AutoSchedule and LoadJSON, after the listeners.
Methods
AddTask method
function AddTask(const Caption: string; StartsAt, EndsAt: TDateTime; Progress: Double = 0): Integer;Adds a task and returns its ID. StartsAt must be at least 1 as a TDateTime (31 December 1899) and before EndsAt, which must be no later than 31 December 9999; Progress is a percentage in 0..100. Otherwise EAtlasGraphError (Invalid task interval or progress) is raised. Fires the listeners and OnChange.
Start := EncodeDate(2026, 9, 21);
Design := Plan.AddTask('Design', Start, Start + 2, 100); // two days, finished
Build := Plan.AddTask('Build', Start, Start + 5, 40);UpdateTask method
procedure UpdateTask(ID: Integer; const Caption: string; StartsAt, EndsAt: TDateTime; Progress: Double);Replaces caption, interval and progress of task ID with the same validation as AddTask; the caption is also written to the graph node. Raises EAtlasGraphError for an unknown ID or invalid values, leaving the task unchanged. Dependencies are kept. Fires the listeners and OnChange.
Task := Plan.Task(ID);
Plan.UpdateTask(ID, Task.Caption, Task.StartsAt, Task.EndsAt, 75); // progress onlyDeleteTask method
procedure DeleteTask(ID: Integer);Removes task ID together with every dependency that starts or ends at it. Raises EAtlasGraphError for an unknown ID. Fires the listeners and OnChange.
Task method
function Task(ID: Integer): TAtlasTask;Returns a copy of the task record with the given ID. Raises EAtlasGraphError (Unknown task) when it does not exist.
Tasks method
function Tasks: TArray<TAtlasTask>;Copies of all tasks in creation order (the order of the graph nodes), which is also the row order of TAtlasGantt. AutoSchedule does not reorder them.
for Task in Plan.Tasks do
Writeln(Task.Caption, ' ', DateToStr(Task.StartsAt), ' - ', DateToStr(Task.EndsAt));Dependencies method
function Dependencies: TArray<TAtlasGraphEdge>;All dependencies as TAtlasGraphEdge records (declared in Atlas.Graph): SourceID is the predecessor task and TargetID the successor. The order is the order in which they were added.
for Edge in Plan.Dependencies do
Writeln(Plan.Task(Edge.SourceID).Caption, ' -> ', Plan.Task(Edge.TargetID).Caption);AddDependency method
procedure AddDependency(PredecessorID, SuccessorID: Integer);Adds a finish-to-start dependency: SuccessorID may start once PredecessorID has ended. The edge is added and the plan's topological order is recomputed; when that fails because the edge closes a cycle, the edge is removed again and EAtlasGraphError (Graph contains a cycle) is raised. Unknown IDs, a self dependency (Self edges are not supported) and a duplicate (Duplicate edge) are also rejected. Adding a dependency does not move any task; call AutoSchedule for that. Fires the listeners and OnChange on success.
Plan.AddDependency(Design, Build);
try
Plan.AddDependency(Build, Design); // would close a cycle
except
on E: EAtlasGraphError do Writeln(E.Message); // the plan is unchanged
end;DeleteDependency method
procedure DeleteDependency(PredecessorID, SuccessorID: Integer);Removes the dependency from PredecessorID to SuccessorID. Raises EAtlasGraphError (Unknown edge) when there is no such dependency. Fires the listeners and OnChange.
AutoSchedule method
procedure AutoSchedule(ProjectStart: TDateTime);Recomputes every task's dates in topological order: a task without predecessors starts at ProjectStart; a task with predecessors starts at the latest EndsAt among them (or ProjectStart if that is later). Each task keeps its duration and progress. The result is built on a copy and swapped in only when every task remains valid, so a chain that would run past the year 9999 leaves the plan untouched. Raises EAtlasGraphError when ProjectStart is outside 1900..9999 or the result is invalid. Calendar days only: no weekends, holidays or working hours. Fires the listeners and OnChange.
Plan.AutoSchedule(EncodeDate(2026, 9, 21));
// Design 21-23 Sep, Build 23-28 Sep, Verify 28-30 Sep for a Design -> Build -> Verify chainToJSON method
function ToJSON: string;Serializes the plan as an object with two keys: graph, the internal TAtlasGraph JSON (nodes with id, caption, x, y, width and height, and edges with source and target), and tasks, an array of objects with id, caption, start, end (TDateTime numbers) and progress. The task caption is stored in both places.
LoadJSON method
procedure LoadJSON(const Text: string);Replaces the plan with the content of a ToJSON string. The import is staged and validated as a whole: the text must be an object with a graph object and a tasks array, the graph must load and be acyclic, every task object must have all five keys with a valid interval and progress, its caption must equal the caption of the graph node with the same ID, IDs must be unique and every graph node must have a task. On any failure EAtlasGraphError is raised and the current plan is kept. Fires the listeners and OnChange once on success.
try
Plan.LoadJSON(TFile.ReadAllText('plan.json'));
except
on E: EAtlasGraphError do
ShowMessage('Plan file rejected: ' + E.Message); // nothing was changed
end;AddListener method
procedure AddListener(const Listener: TNotifyEvent);Registers a TNotifyEvent called with the plan as Sender after every change, before OnChange. The same method of the same object is registered once; nil is ignored.
RemoveListener method
procedure RemoveListener(const Listener: TNotifyEvent);Removes a listener registered with AddListener.
Examples
Schedule a three-task chain
Tasks are created at the project start; AutoSchedule moves each one behind its predecessor while keeping durations and progress.
uses System.SysUtils, Atlas.Gantt;
procedure PlanRelease;
var
Plan: TAtlasTaskPlan;
Start: TDateTime;
Design, Build, Verify: Integer;
Task: TAtlasTask;
begin
Plan := TAtlasTaskPlan.Create(nil);
try
Start := EncodeDate(2026, 9, 21);
Design := Plan.AddTask('Design', Start, Start + 2, 100);
Build := Plan.AddTask('Build', Start, Start + 5, 40);
Verify := Plan.AddTask('Verify', Start, Start + 2);
Plan.AddDependency(Design, Build);
Plan.AddDependency(Build, Verify);
Plan.AutoSchedule(Start);
for Task in Plan.Tasks do
Writeln(Format('%-7s %s - %s %3.0f%%', [Task.Caption,
FormatDateTime('dd mmm', Task.StartsAt), FormatDateTime('dd mmm', Task.EndsAt), Task.Progress]));
// Design 21 Sep - 23 Sep 100%
// Build 23 Sep - 28 Sep 40%
// Verify 28 Sep - 30 Sep 0%
finally
Plan.Free;
end;
end;Related
TAtlasTask · TAtlasGantt · TAtlasGraph
Also in Atlas VCL
TAtlasTable · TAtlasGrid · TAtlasValueEdit · TAtlasTree · TAtlasWorkbook · TAtlasSheetGrid · TAtlasPivot · TAtlasInspector · TAtlasRichEdit · TAtlasSpellChecker · TAtlasChart · TAtlasGauge · TAtlasThemer · TAtlasWizard · TAtlasWorkspace · TAtlasCommandBar · TAtlasActionTile · TAtlasTileBoard · TAtlasNavigation · TAtlasMap · TAtlasGraph · TAtlasDiagram · TAtlasPrintDocument · TAtlasSchedule · TAtlasScheduler · TAtlasGantt · TAtlasPdfDocument · TAtlasPdfViewer