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