YOUSEFUL / DELPHI / COMPONENTS

TAtlasTaskPlan

Task model with intervals, progress, acyclic finish-to-start dependencies, staged automatic scheduling and transactional JSON.

Non-visual component Palette: Atlas Unit Atlas.Gantt · Inherits TComponent · Product Atlas VCL

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

MemberKindWhat it does
CreateconstructorCreates an empty plan with its own internal dependency graph.
DestroydestructorFrees the tasks, the graph and the listener list; attached TAtlasGantt controls detach through the component free notification.
AddTaskmethodAdds a task and returns its ID.
UpdateTaskmethodReplaces caption, interval and progress of task ID with the same validation as AddTask; the caption is also written to the graph node.
DeleteTaskmethodRemoves task ID together with every dependency that starts or ends at it.
TaskmethodReturns a copy of the task record with the given ID.
TasksmethodCopies of all tasks in creation order (the order of the graph nodes), which is also the row order of TAtlasGantt.
DependenciesmethodAll dependencies as TAtlasGraphEdge records (declared in Atlas.Graph): SourceID is the predecessor task and TargetID the successor.
AddDependencymethodAdds a finish-to-start dependency: SuccessorID may start once PredecessorID has ended.
DeleteDependencymethodRemoves the dependency from PredecessorID to SuccessorID.
AutoSchedulemethodRecomputes 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).
ToJSONmethodSerializes 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.
LoadJSONmethodReplaces the plan with the content of a ToJSON string.
AddListenermethodRegisters a TNotifyEvent called with the plan as Sender after every change, before OnChange.
RemoveListenermethodRemoves a listener registered with AddListener.
OnChangeeventFires 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.

pascal
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.

pascal
Task := Plan.Task(ID);
Plan.UpdateTask(ID, Task.Caption, Task.StartsAt, Task.EndsAt, 75);   // progress only

DeleteTask 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.

pascal
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.

pascal
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.

pascal
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.

pascal
Plan.AutoSchedule(EncodeDate(2026, 9, 21));
// Design 21-23 Sep, Build 23-28 Sep, Verify 28-30 Sep for a Design -> Build -> Verify chain

ToJSON 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.

pascal
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.

pascal
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;

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