Line data Source code
1 : #pragma once
2 :
3 : #include "gui/commands/command.h"
4 : #include <vector>
5 : #include <memory>
6 :
7 : namespace Amplitron {
8 :
9 : /**
10 : * @brief Manages undo and redo stacks for Command objects.
11 : *
12 : * Provides execute(), undo(), redo(), and push_executed() operations.
13 : * Supports coalescing of rapid parameter changes and enforces a
14 : * configurable maximum history depth (default DEFAULT_MAX_DEPTH).
15 : */
16 92 : class CommandHistory {
17 : public:
18 : /** @brief Default maximum number of undo entries. */
19 : static constexpr int DEFAULT_MAX_DEPTH = 100;
20 :
21 : /**
22 : * @brief Construct a CommandHistory.
23 : * @param max_depth Maximum undo stack size. Negative values are clamped to 0.
24 : */
25 368 : explicit CommandHistory(int max_depth = DEFAULT_MAX_DEPTH)
26 368 : : max_depth_(max_depth < 0 ? 0 : max_depth) {}
27 :
28 : /**
29 : * @brief Execute a command and push it onto the undo stack.
30 : *
31 : * Calls cmd->execute(), attempts coalescing with the stack top, and
32 : * clears the redo stack (new action invalidates the redo branch).
33 : *
34 : * @param cmd Owning pointer to the command to execute.
35 : */
36 : void execute(std::unique_ptr<Command> cmd);
37 :
38 : /**
39 : * @brief Record a command that was already applied by the caller.
40 : *
41 : * Useful for knob changes that are applied directly by the widget;
42 : * the command is pushed for undo without calling execute().
43 : *
44 : * @param cmd Owning pointer to the already-executed command.
45 : */
46 : void push_executed(std::unique_ptr<Command> cmd);
47 :
48 : /**
49 : * @brief Undo the most recent command.
50 : * @return true if an undo was performed, false if the stack was empty.
51 : */
52 : bool undo();
53 :
54 : /**
55 : * @brief Redo the most recently undone command.
56 : * @return true if a redo was performed, false if the stack was empty.
57 : */
58 : bool redo();
59 :
60 : /** @brief Clear both undo and redo stacks (e.g. when loading a preset). */
61 : void clear();
62 :
63 : /** @brief Return true if the undo stack is non-empty. */
64 3 : bool can_undo() const { return !undo_stack_.empty(); }
65 :
66 : /** @brief Return true if the redo stack is non-empty. */
67 9 : bool can_redo() const { return !redo_stack_.empty(); }
68 :
69 : /** @brief Number of commands on the undo stack. */
70 30 : int undo_size() const { return static_cast<int>(undo_stack_.size()); }
71 :
72 : /** @brief Number of commands on the redo stack. */
73 3 : int redo_size() const { return static_cast<int>(redo_stack_.size()); }
74 :
75 : /** @brief Current maximum undo depth. */
76 : int max_depth() const { return max_depth_; }
77 :
78 : /**
79 : * @brief Change the maximum undo depth and trim excess entries.
80 : * @param depth New maximum depth. Negative values are clamped to 0.
81 : */
82 : void set_max_depth(int depth) { max_depth_ = (depth < 0) ? 0 : depth; trim(); }
83 :
84 : /** @brief Description string of the top undo command, or nullptr if empty. */
85 : const char* undo_description() const;
86 :
87 : /** @brief Description string of the top redo command, or nullptr if empty. */
88 : const char* redo_description() const;
89 :
90 : private:
91 : /** @brief Evict the oldest undo entries until the stack fits max_depth_. */
92 : void trim();
93 :
94 : std::vector<std::unique_ptr<Command>> undo_stack_;
95 : std::vector<std::unique_ptr<Command>> redo_stack_;
96 : int max_depth_;
97 : };
98 :
99 : } // namespace Amplitron
|