Olympe Engine 2.0
2D Game Engine with ECS Architecture
Loading...
Searching...
No Matches
UndoRedoStack.h
Go to the documentation of this file.
1/**
2 * @file UndoRedoStack.h
3 * @brief Undo/Redo command stack for ATS Visual Scripting editor (Phase 6).
4 * @author Olympe Engine
5 * @date 2026-03-09
6 *
7 * @details
8 * Implements the Command pattern for reversible graph editing operations.
9 * Supported commands: AddNode, DeleteNode, MoveNode, AddConnection.
10 * Stack size is bounded to MAX_STACK_SIZE (100) to cap memory usage.
11 *
12 * C++14 compliant — no std::optional, structured bindings, std::filesystem.
13 */
14
15#pragma once
16
17#include <string>
18#include <vector>
19#include <memory>
20#include <cstddef>
21
22#include "../TaskSystem/TaskGraphTemplate.h"
23
24namespace Olympe {
25
26// ============================================================================
27// ICommand — abstract base
28// ============================================================================
29
30/**
31 * @interface ICommand
32 * @brief Abstract base for all reversible editor commands.
33 */
34class ICommand {
35public:
36 virtual ~ICommand() = default;
37
38 /**
39 * @brief Applies the command to the graph.
40 */
41 virtual void Execute(TaskGraphTemplate& graph) = 0;
42
43 /**
44 * @brief Reverses the command on the graph.
45 */
46 virtual void Undo(TaskGraphTemplate& graph) = 0;
47
48 /**
49 * @brief Returns a short human-readable description (e.g. "Add Node #3").
50 */
51 virtual std::string GetDescription() const = 0;
52};
53
54// ============================================================================
55// Concrete commands
56// ============================================================================
57
58/**
59 * @class AddNodeCommand
60 * @brief Records an "add node" operation for undo/redo.
61 */
62class AddNodeCommand : public ICommand {
63public:
65
66 void Execute(TaskGraphTemplate& graph) override;
67 void Undo(TaskGraphTemplate& graph) override;
68 std::string GetDescription() const override;
69
70private:
72};
73
74/**
75 * @class DeleteNodeCommand
76 * @brief Records a "delete node" operation for undo/redo.
77 *
78 * @details
79 * On Execute() the node is removed from the graph (and any exec/data
80 * connections referencing it are also removed).
81 * On Undo() the node and its connections are restored.
82 */
83class DeleteNodeCommand : public ICommand {
84public:
85 explicit DeleteNodeCommand(int32_t nodeID);
86
87 void Execute(TaskGraphTemplate& graph) override;
88 void Undo(TaskGraphTemplate& graph) override;
89 std::string GetDescription() const override;
90
91private:
94 std::vector<ExecPinConnection> m_savedExecConns;
95 std::vector<DataPinConnection> m_savedDataConns;
96};
97
98/**
99 * @class MoveNodeCommand
100 * @brief Records a "move node" (position change) for undo/redo.
101 *
102 * @details
103 * Node position is stored in the Parameters map under the keys
104 * "__posX" and "__posY" (editor-internal convention).
105 */
106class MoveNodeCommand : public ICommand {
107public:
108 MoveNodeCommand(int32_t nodeID, float oldX, float oldY, float newX, float newY);
109
110 void Execute(TaskGraphTemplate& graph) override;
111 void Undo(TaskGraphTemplate& graph) override;
112 std::string GetDescription() const override;
113
114private:
118
119 static void SetNodePos(TaskGraphTemplate& graph, int32_t nodeID, float x, float y);
120};
121
122/**
123 * @class AddConnectionCommand
124 * @brief Records an "add exec connection" operation for undo/redo.
125 */
127public:
129
130 void Execute(TaskGraphTemplate& graph) override;
131 void Undo(TaskGraphTemplate& graph) override;
132 std::string GetDescription() const override;
133
134private:
136};
137
138/**
139 * @class AddDataConnectionCommand
140 * @brief Records an "add data connection" operation for undo/redo.
141 */
143public:
145
146 void Execute(TaskGraphTemplate& graph) override;
147 void Undo(TaskGraphTemplate& graph) override;
148 std::string GetDescription() const override;
149
150private:
152};
153
154/**
155 * @class DeleteLinkCommand
156 * @brief Records a "delete link" operation for undo/redo.
157 *
158 * @details
159 * Supports both exec and data connections. On Execute() the connection is
160 * removed from the graph. On Undo() it is restored.
161 */
163public:
166
167 void Execute(TaskGraphTemplate& graph) override;
168 void Undo(TaskGraphTemplate& graph) override;
169 std::string GetDescription() const override;
170
171private:
172 bool m_isExecConn = true;
175};
176
177// ============================================================================
178// PropertyValue — typed value union for EditNodePropertyCommand (C++14)
179// ============================================================================
180
181/**
182 * @struct PropertyValue
183 * @brief Discriminated union of property value types (String / Float).
184 *
185 * C++14 compliant — no std::variant, no std::optional.
186 */
188 enum class Kind { String, Float, Int } kind;
189 std::string strVal;
190 float floatVal;
192
194
195 static PropertyValue FromString(const std::string& s)
196 {
197 PropertyValue v; v.kind = Kind::String; v.strVal = s; return v;
198 }
199
201 {
202 PropertyValue v; v.kind = Kind::Float; v.floatVal = f; return v;
203 }
204
206 {
207 PropertyValue v; v.kind = Kind::Int; v.intVal = i; return v;
208 }
209};
210
211// ============================================================================
212// EditNodePropertyCommand
213// ============================================================================
214
215/**
216 * @class EditNodePropertyCommand
217 * @brief Records a property edit on a single node for undo/redo.
218 *
219 * @details
220 * Covers all editable fields exposed by the Properties panel:
221 * NodeName, AtomicTaskID, DelaySeconds, ConditionID, BBKey,
222 * MathOperator, SubGraphPath.
223 *
224 * Uses the "commit on release" pattern: the command is pushed only when the
225 * ImGui widget loses focus after an edit (IsItemDeactivatedAfterEdit), so
226 * a single undo entry is created per editing session, not one per keystroke.
227 */
229public:
231 const std::string& propertyKey,
232 const PropertyValue& oldValue,
233 const PropertyValue& newValue);
234
235 void Execute(TaskGraphTemplate& graph) override;
236 void Undo(TaskGraphTemplate& graph) override;
237 std::string GetDescription() const override;
238
239private:
241 std::string m_propertyKey;
244
245 /// Applies @p value to the named field of @p node.
247 const std::string& key,
248 const PropertyValue& value);
249};
250
251// ============================================================================
252// AddDynamicPinCommand
253// ============================================================================
254
255/**
256 * @class AddDynamicPinCommand
257 * @brief Records "add dynamic exec-out pin" on a VSSequence or VSSwitch node for undo/redo.
258 *
259 * Execute() adds a new pin name (e.g. "Out_2" or "Case_2") to the node's
260 * DynamicExecOutputPins vector and rebuilds the lookup cache.
261 * Undo() removes the last pin.
262 */
264public:
265 AddDynamicPinCommand(int32_t nodeID, const std::string& pinName);
266
267 void Execute(TaskGraphTemplate& graph) override;
268 void Undo(TaskGraphTemplate& graph) override;
269 std::string GetDescription() const override;
270
271private:
273 std::string m_pinName;
274};
275
276// ============================================================================
277// RemoveExecPinCommand
278// ============================================================================
279
280/**
281 * @class RemoveExecPinCommand
282 * @brief Records "remove dynamic exec-out pin" on a VSSequence or VSSwitch node for undo/redo.
283 *
284 * Execute() removes the pin from DynamicExecOutputPins at the stored index,
285 * and removes any ExecConnection originating from that pin.
286 * Undo() re-inserts the pin at its original index and restores any saved
287 * connection.
288 *
289 * @details
290 * Stored data:
291 * - m_nodeID : owning node
292 * - m_pinName : pin name (e.g. "Out_1", "Case_2")
293 * - m_pinIndex : 0-based index within DynamicExecOutputPins
294 * - m_linkedTargetNodeID : target node of the outgoing link, or -1 if none
295 * - m_linkedTargetPinName : target pin name of the outgoing link
296 */
298public:
300 const std::string& pinName,
301 int pinIndex,
303 const std::string& linkedTargetPinName);
304
305 void Execute(TaskGraphTemplate& graph) override;
306 void Undo(TaskGraphTemplate& graph) override;
307 std::string GetDescription() const override;
308
309private:
311 std::string m_pinName;
313 int32_t m_linkedTargetNodeID; ///< -1 if no outgoing link was present
315};
316
317// ============================================================================
318// EditParameterCommand (Phase 22-C)
319// ============================================================================
320
321/**
322 * @class EditParameterCommand
323 * @brief Records an edit to a single named parameter binding on a node.
324 *
325 * Stores the old and new ParameterBinding (type + literal value + variable name)
326 * so that the change can be reversed via Undo() and re-applied via Execute().
327 */
328class EditParameterCommand : public ICommand {
329public:
331 const std::string& paramName,
334
335 void Execute(TaskGraphTemplate& graph) override;
336 void Undo(TaskGraphTemplate& graph) override;
337 std::string GetDescription() const override;
338
339private:
341 std::string m_paramName;
344};
345
346// ============================================================================
347// EditNodePropertiesCommand (Phase 22-C)
348// ============================================================================
349
350/**
351 * @class EditNodePropertiesCommand
352 * @brief Records a batch edit of all parameter bindings on a node.
353 *
354 * Suitable for snapshotting the full parameter map before a complex edit and
355 * restoring it atomically on Undo().
356 */
358public:
359 using ParameterMap = std::unordered_map<std::string, ParameterBinding>;
360
362 const ParameterMap& oldParams,
363 const ParameterMap& newParams);
364
365 void Execute(TaskGraphTemplate& graph) override;
366 void Undo(TaskGraphTemplate& graph) override;
367 std::string GetDescription() const override;
368
369private:
373};
374
375// ============================================================================
376// UndoRedoStack
377// ============================================================================
378
379/**
380 * @class UndoRedoStack
381 * @brief Bounded stack of reversible editor commands.
382 *
383 * @details
384 * Usage:
385 * @code
386 * UndoRedoStack stack;
387 * stack.PushCommand(std::unique_ptr<ICommand>(new AddNodeCommand(def)));
388 * if (stack.CanUndo()) stack.Undo(graph);
389 * if (stack.CanRedo()) stack.Redo(graph);
390 * @endcode
391 *
392 * When more than MAX_STACK_SIZE commands are pushed, the oldest undo entry is
393 * dropped (FIFO eviction). Pushing any new command clears the redo stack.
394 */
396public:
397
398 /// Maximum number of undo entries kept in memory.
399 static const std::size_t MAX_STACK_SIZE = 100u;
400
402
403 /**
404 * @brief Executes the command on @p graph, then pushes it onto the undo
405 * stack. Clears the redo stack.
406 *
407 * @param cmd Owning pointer to the command. Must not be null.
408 * @param graph Target graph on which Execute() is called immediately.
409 */
410 void PushCommand(std::unique_ptr<ICommand> cmd, TaskGraphTemplate& graph);
411
412 /**
413 * @brief Undoes the last command, moving it to the redo stack.
414 *
415 * @pre CanUndo() == true
416 */
418
419 /**
420 * @brief Re-applies the last undone command, moving it back to the undo
421 * stack.
422 *
423 * @pre CanRedo() == true
424 */
426
427 /**
428 * @brief Clears both undo and redo stacks (e.g. after save).
429 */
430 void Clear();
431
432 /** @brief Returns true if there is at least one command to undo. */
433 bool CanUndo() const;
434
435 /** @brief Returns true if there is at least one command to redo. */
436 bool CanRedo() const;
437
438 /** @brief Returns the number of commands currently on the undo stack. */
439 std::size_t UndoSize() const;
440
441 /** @brief Returns the number of commands currently on the redo stack. */
442 std::size_t RedoSize() const;
443
444 /**
445 * @brief Returns the description of the top undo command, or "" if empty.
446 */
447 std::string PeekUndoDescription() const;
448
449 /**
450 * @brief Returns the description of the top redo command, or "" if empty.
451 */
452 std::string PeekRedoDescription() const;
453
454private:
455 std::vector<std::unique_ptr<ICommand>> m_undoStack;
456 std::vector<std::unique_ptr<ICommand>> m_redoStack;
457};
458
459} // namespace Olympe
ComponentTypeID GetComponentTypeID_Static()
Definition ECS_Entity.h:56
Records an "add exec connection" operation for undo/redo.
std::string GetDescription() const override
Returns a short human-readable description (e.g.
void Execute(TaskGraphTemplate &graph) override
Applies the command to the graph.
void Undo(TaskGraphTemplate &graph) override
Reverses the command on the graph.
Records an "add data connection" operation for undo/redo.
void Undo(TaskGraphTemplate &graph) override
Reverses the command on the graph.
std::string GetDescription() const override
Returns a short human-readable description (e.g.
void Execute(TaskGraphTemplate &graph) override
Applies the command to the graph.
Records "add dynamic exec-out pin" on a VSSequence or VSSwitch node for undo/redo.
void Execute(TaskGraphTemplate &graph) override
Applies the command to the graph.
std::string GetDescription() const override
Returns a short human-readable description (e.g.
void Undo(TaskGraphTemplate &graph) override
Reverses the command on the graph.
Command to add a node to the tree.
TaskNodeDefinition m_node
std::string GetDescription() const override
Get a human-readable description of the command.
void Undo() override
Undo the command.
void Execute() override
Execute the command.
Records a "delete link" operation for undo/redo.
void Execute(TaskGraphTemplate &graph) override
Applies the command to the graph.
DataPinConnection m_savedDataConn
ExecPinConnection m_savedExecConn
void Undo(TaskGraphTemplate &graph) override
Reverses the command on the graph.
std::string GetDescription() const override
Returns a short human-readable description (e.g.
Command to delete a node from the tree.
TaskNodeDefinition m_savedNode
void Undo() override
Undo the command.
std::vector< DataPinConnection > m_savedDataConns
void Execute() override
Execute the command.
std::string GetDescription() const override
Get a human-readable description of the command.
std::vector< ExecPinConnection > m_savedExecConns
Records a batch edit of all parameter bindings on a node.
std::string GetDescription() const override
Returns a short human-readable description (e.g.
void Execute(TaskGraphTemplate &graph) override
Applies the command to the graph.
void Undo(TaskGraphTemplate &graph) override
Reverses the command on the graph.
std::unordered_map< std::string, ParameterBinding > ParameterMap
Records a property edit on a single node for undo/redo.
void Undo(TaskGraphTemplate &graph) override
Reverses the command on the graph.
void Execute(TaskGraphTemplate &graph) override
Applies the command to the graph.
static void ApplyValue(TaskNodeDefinition &node, const std::string &key, const PropertyValue &value)
Applies value to the named field of node.
std::string GetDescription() const override
Returns a short human-readable description (e.g.
Command to edit a node parameter.
std::string GetDescription() const override
Get a human-readable description of the command.
void Undo() override
Undo the command.
void Execute() override
Execute the command.
Abstract base for all reversible editor commands.
virtual std::string GetDescription() const =0
Returns a short human-readable description (e.g.
virtual void Undo(TaskGraphTemplate &graph)=0
Reverses the command on the graph.
virtual void Execute(TaskGraphTemplate &graph)=0
Applies the command to the graph.
virtual ~ICommand()=default
Command to move a node.
std::string GetDescription() const override
Get a human-readable description of the command.
void Undo() override
Undo the command.
static void SetNodePos(TaskGraphTemplate &graph, int32_t nodeID, float x, float y)
void Execute() override
Execute the command.
Records "remove dynamic exec-out pin" on a VSSequence or VSSwitch node for undo/redo.
std::string GetDescription() const override
Returns a short human-readable description (e.g.
int32_t m_linkedTargetNodeID
-1 if no outgoing link was present
void Execute(TaskGraphTemplate &graph) override
Applies the command to the graph.
void Undo(TaskGraphTemplate &graph) override
Reverses the command on the graph.
Immutable, shareable task graph asset.
Bounded stack of reversible editor commands.
bool CanUndo() const
Returns true if there is at least one command to undo.
std::size_t RedoSize() const
Returns the number of commands currently on the redo stack.
void Clear()
Clears both undo and redo stacks (e.g.
std::size_t UndoSize() const
Returns the number of commands currently on the undo stack.
bool CanRedo() const
Returns true if there is at least one command to redo.
std::string PeekUndoDescription() const
Returns the description of the top undo command, or "" if empty.
void Redo(TaskGraphTemplate &graph)
Re-applies the last undone command, moving it back to the undo stack.
std::string PeekRedoDescription() const
Returns the description of the top redo command, or "" if empty.
void Undo(TaskGraphTemplate &graph)
Undoes the last command, moving it to the redo stack.
std::vector< std::unique_ptr< ICommand > > m_undoStack
std::vector< std::unique_ptr< ICommand > > m_redoStack
void PushCommand(std::unique_ptr< ICommand > cmd, TaskGraphTemplate &graph)
Executes the command on graph, then pushes it onto the undo stack.
static const std::size_t MAX_STACK_SIZE
Maximum number of undo entries kept in memory.
< Provides AssetID and INVALID_ASSET_ID
Explicit connection between an output data pin of a source node and an input data pin of a target nod...
Explicit connection between a named exec-out pin of a source node and the exec-in pin of a target nod...
Describes how a single parameter value is supplied to a task node.
Discriminated union of property value types (String / Float).
static PropertyValue FromFloat(float f)
enum Olympe::PropertyValue::Kind kind
static PropertyValue FromString(const std::string &s)
static PropertyValue FromInt(int i)
Full description of a single node in the task graph.