Skip to content

Commit c901789

Browse files
committed
[core] Implement a command transaction system
1 parent f1c441e commit c901789

File tree

15 files changed

+170
-40
lines changed

15 files changed

+170
-40
lines changed

src/lib/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,7 @@ set(SRCS
383383
"${CMAKE_CURRENT_SOURCE_DIR}/score/command/CommandGeneratorMap.cpp"
384384
"${CMAKE_CURRENT_SOURCE_DIR}/score/command/PropertyCommand.cpp"
385385
"${CMAKE_CURRENT_SOURCE_DIR}/score/command/Validity/ValidityChecker.cpp"
386+
"${CMAKE_CURRENT_SOURCE_DIR}/score/command/Dispatchers/SendStrategy.cpp"
386387

387388
"${CMAKE_CURRENT_SOURCE_DIR}/score/model/Component.cpp"
388389
"${CMAKE_CURRENT_SOURCE_DIR}/score/model/ColorInterpolator.cpp"

src/lib/core/command/CommandStack.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ void CommandStack::setIndex(int index)
119119

120120
void CommandStack::undoQuiet()
121121
{
122+
CommandTransaction t{*this};
122123
updateStack([&]() {
123124
auto cmd = m_undoable.pop();
124125
cmd->undo(m_ctx);
@@ -131,6 +132,7 @@ void CommandStack::undoQuiet()
131132

132133
void CommandStack::redoQuiet()
133134
{
135+
CommandTransaction t{*this};
134136
updateStack([&]() {
135137
auto cmd = m_redoable.pop();
136138
cmd->redo(m_ctx);
@@ -212,12 +214,17 @@ void CommandStack::validateDocument() const
212214
m_checker();
213215
}
214216

215-
CommandStackFacade::CommandStackFacade(CommandStack& stack)
217+
CommandTransaction CommandStack::transaction()
218+
{
219+
return CommandTransaction{*this};
220+
}
221+
222+
CommandStackFacade::CommandStackFacade(CommandStack& stack) noexcept
216223
: m_stack{stack}
217224
{
218225
}
219226

220-
const DocumentContext& CommandStackFacade::context() const
227+
const DocumentContext& CommandStackFacade::context() const noexcept
221228
{
222229
return m_stack.context();
223230
}
@@ -242,4 +249,8 @@ void CommandStackFacade::enableActions() const
242249
m_stack.enableActions();
243250
}
244251

252+
CommandTransaction CommandStackFacade::transaction() const
253+
{
254+
return m_stack.transaction();
255+
}
245256
}

src/lib/core/command/CommandStack.hpp

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
namespace score
1212
{
1313
class Document;
14-
14+
struct CommandTransaction;
1515
/**
1616
* \class score::CommandStack
1717
*
@@ -123,6 +123,9 @@ class SCORE_LIB_BASE_EXPORT CommandStack final : public QObject
123123
void redoQuiet();
124124
W_INVOKABLE(redoQuiet)
125125

126+
void beginTransaction() E_SIGNAL(SCORE_LIB_BASE_EXPORT, beginTransaction)
127+
void endTransaction() E_SIGNAL(SCORE_LIB_BASE_EXPORT, endTransaction)
128+
126129
/**
127130
* @brief push Pushes a command on the stack
128131
* @param cmd The command
@@ -205,14 +208,48 @@ class SCORE_LIB_BASE_EXPORT CommandStack final : public QObject
205208

206209
void validateDocument() const;
207210

211+
CommandTransaction transaction();
212+
208213
private:
214+
friend struct CommandTransaction;
209215
QStack<score::Command*> m_undoable;
210216
QStack<score::Command*> m_redoable;
211217

212218
int m_savedIndex{};
219+
int m_inTransaction{};
213220

214221
DocumentValidator m_checker;
215222
const score::DocumentContext& m_ctx;
216223
};
224+
225+
struct CommandTransaction
226+
{
227+
CommandStack& self;
228+
229+
explicit CommandTransaction(CommandStack& self)
230+
: self{self}
231+
{
232+
if(self.m_inTransaction == 0)
233+
{
234+
self.beginTransaction();
235+
}
236+
self.m_inTransaction++;
237+
}
238+
CommandTransaction(const CommandTransaction& other) = delete;
239+
CommandTransaction(CommandTransaction&& other) = delete;
240+
CommandTransaction& operator=(const CommandTransaction& other) = delete;
241+
CommandTransaction& operator=(CommandTransaction&& other) = delete;
242+
243+
~CommandTransaction()
244+
{
245+
SCORE_ASSERT(self.m_inTransaction > 0);
246+
247+
self.m_inTransaction--;
248+
if(self.m_inTransaction == 0)
249+
{
250+
self.endTransaction();
251+
}
252+
}
253+
};
217254
}
218255
W_REGISTER_ARGTYPE(score::Command*)

src/lib/core/document/DocumentBuilder.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,10 @@ Document* DocumentBuilder::restoreDocument(
192192
ctx.components, writer, doc->commandStack(), [doc](score::Command* cmd) {
193193
try
194194
{
195+
qDebug() << ".. replaying: " << cmd->key().toString().c_str()
196+
<< cmd->description();
195197
cmd->redo(doc->context());
198+
qApp->processEvents();
196199
return true;
197200
}
198201
catch(...)

src/lib/score/command/CommandStackFacade.hpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ namespace score
55
{
66
class Command;
77
class CommandStack;
8+
struct CommandTransaction;
89
struct DocumentContext;
910

1011
/**
@@ -22,14 +23,16 @@ class SCORE_LIB_BASE_EXPORT CommandStackFacade
2223
score::CommandStack& m_stack;
2324

2425
public:
25-
explicit CommandStackFacade(score::CommandStack& stack);
26+
explicit CommandStackFacade(score::CommandStack& stack) noexcept;
2627

27-
const score::DocumentContext& context() const;
28+
const score::DocumentContext& context() const noexcept;
2829

2930
void push(score::Command* cmd) const;
3031
void redoAndPush(score::Command* cmd) const;
3132

3233
void disableActions() const;
3334
void enableActions() const;
35+
36+
CommandTransaction transaction() const;
3437
};
3538
}

src/lib/score/command/Dispatchers/OngoingCommandDispatcher.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#pragma once
2+
#include <score/command/CommandStackFacade.hpp>
23
#include <score/command/Dispatchers/ICommandDispatcher.hpp>
34
#include <score/command/Dispatchers/SendStrategy.hpp>
45
#include <score/plugins/StringFactoryKey.hpp>
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#include "SendStrategy.hpp"
2+
3+
#include <score/command/CommandStackFacade.hpp>
4+
#include <score/document/DocumentContext.hpp>
5+
6+
#include <core/command/CommandStack.hpp>
7+
8+
namespace SendStrategy
9+
{
10+
11+
void Simple::send(const score::CommandStackFacade& stack, score::Command* other)
12+
{
13+
auto trans = stack.context().commandStack.transaction();
14+
stack.redoAndPush(other);
15+
}
16+
17+
void UndoRedo::send(const score::CommandStackFacade& stack, score::Command* other)
18+
{
19+
auto trans = stack.context().commandStack.transaction();
20+
other->undo(stack.context());
21+
stack.redoAndPush(other);
22+
}
23+
24+
void Quiet::send(const score::CommandStackFacade& stack, score::Command* other)
25+
{
26+
stack.push(other);
27+
}
28+
29+
}
30+
namespace RedoStrategy
31+
{
32+
void Redo::redo(const score::DocumentContext& ctx, score::Command& cmd)
33+
{
34+
auto trans = ctx.commandStack.transaction();
35+
cmd.redo(ctx);
36+
}
37+
38+
void Quiet::redo(const score::DocumentContext& ctx, score::Command& cmd) { }
39+
40+
}
Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,38 @@
11
#pragma once
2-
#include <score/command/CommandStackFacade.hpp>
2+
#include <score_lib_base_export.h>
3+
namespace score
4+
{
5+
class CommandStackFacade;
6+
class Command;
7+
struct DocumentContext;
8+
}
39

410
namespace SendStrategy
511
{
6-
struct Simple
12+
struct SCORE_LIB_BASE_EXPORT Simple
713
{
8-
static void send(const score::CommandStackFacade& stack, score::Command* other)
9-
{
10-
stack.redoAndPush(other);
11-
}
14+
static void send(const score::CommandStackFacade& stack, score::Command* other);
1215
};
1316

14-
struct Quiet
17+
struct SCORE_LIB_BASE_EXPORT Quiet
1518
{
16-
static void send(const score::CommandStackFacade& stack, score::Command* other)
17-
{
18-
stack.push(other);
19-
}
19+
static void send(const score::CommandStackFacade& stack, score::Command* other);
2020
};
2121

22-
struct UndoRedo
22+
struct SCORE_LIB_BASE_EXPORT UndoRedo
2323
{
24-
static void send(const score::CommandStackFacade& stack, score::Command* other)
25-
{
26-
other->undo(stack.context());
27-
stack.redoAndPush(other);
28-
}
24+
static void send(const score::CommandStackFacade& stack, score::Command* other);
2925
};
3026
}
3127
namespace RedoStrategy
3228
{
33-
struct Redo
29+
struct SCORE_LIB_BASE_EXPORT Redo
3430
{
35-
static void redo(const score::DocumentContext& ctx, score::Command& cmd)
36-
{
37-
cmd.redo(ctx);
38-
}
31+
static void redo(const score::DocumentContext& ctx, score::Command& cmd);
3932
};
4033

41-
struct Quiet
34+
struct SCORE_LIB_BASE_EXPORT Quiet
4235
{
43-
static void redo(const score::DocumentContext& ctx, score::Command& cmd) { }
36+
static void redo(const score::DocumentContext& ctx, score::Command& cmd);
4437
};
4538
}

src/plugins/score-lib-process/Effect/EffectLayout.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <cmath>
1111

1212
#include <cstdint>
13+
// FIXME put the entirety of this as dynamic behaviour instead
1314
namespace Process
1415
{
1516

src/plugins/score-lib-process/Process/ExecutionContext.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,10 @@ struct SCORE_LIB_PROCESS_EXPORT Context
9696
const std::shared_ptr<ossia::graph_interface>& execGraph;
9797
const std::shared_ptr<ossia::execution_state>& execState;
9898

99+
std::shared_ptr<Execution::Transaction>& transaction;
100+
101+
void execCommand(ExecutionCommand&& cmd);
102+
99103
auto& context() const { return *this; }
100104

101105
#if !defined(_MSC_VER)

src/plugins/score-lib-process/Process/ExecutionSetup.cpp

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,19 @@
1919

2020
namespace Execution
2121
{
22+
void Context::execCommand(ExecutionCommand&& cmd)
23+
{
24+
if(transaction)
25+
transaction->push_back(std::move(cmd));
26+
else
27+
executionQueue.enqueue(std::move(cmd));
28+
}
2229

2330
static auto enqueue_in_context(SetupContext& self) noexcept
2431
{
2532
return [&self]<typename F>(F&& f) {
2633
static_assert(std::is_nothrow_move_constructible_v<F>);
27-
self.context.executionQueue.enqueue(std::move(f));
34+
self.context.execCommand(std::move(f));
2835
};
2936
}
3037

@@ -72,7 +79,7 @@ void SetupContext::on_cableRemoved(const Process::Cable& c)
7279
auto it = m_cables.find(c.id());
7380
if(it != m_cables.end())
7481
{
75-
context.executionQueue.enqueue(
82+
context.execCommand(
7683
[cable = it->second, graph = context.execGraph] { graph->disconnect(cable); });
7784
}
7885
}
@@ -135,7 +142,7 @@ void SetupContext::connectCable(Process::Cable& cable)
135142
}
136143

137144
m_cables[cable.id()] = edge;
138-
context.executionQueue.enqueue([edge, graph = context.execGraph]() mutable {
145+
context.execCommand([edge, graph = context.execGraph]() mutable {
139146
graph->connect(std::move(edge));
140147
});
141148
}
@@ -428,7 +435,7 @@ void SetupContext::unregister_inlet(
428435
if(ossia_port_it != inlets.end())
429436
{
430437
std::weak_ptr<ossia::execution_state> ws = context.execState;
431-
context.executionQueue.enqueue([ws, ossia_port = ossia_port_it->second.second] {
438+
context.execCommand([ws, ossia_port = ossia_port_it->second.second] {
432439
if(auto state = ws.lock())
433440
state->unregister_port(*ossia_port);
434441
});
@@ -533,7 +540,7 @@ void SetupContext::unregister_node(
533540
{
534541
std::weak_ptr<ossia::graph_interface> wg = context.execGraph;
535542
std::weak_ptr<ossia::execution_state> ws = context.execState;
536-
context.executionQueue.enqueue([wg, ws, node] {
543+
context.execCommand([wg, ws, node] {
537544
if(auto s = ws.lock())
538545
{
539546
ossia::for_each_inlet(*node, [&](auto& p) { s->unregister_port(p); });

src/plugins/score-plugin-engine/Execution/DocumentPlugin.cpp

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ DocumentPlugin::ContextData::ContextData(const score::DocumentContext& ctx)
5050
, context
5151
{
5252
{}, ctx, m_created, {}, {}, m_execQueue, m_editionQueue, m_gcQueue, setupContext,
53-
execGraph, execState
53+
execGraph, execState, currentTransaction
5454
#if(__cplusplus > 201703L) && !defined(_MSC_VER)
5555
,
5656
{
@@ -91,6 +91,34 @@ DocumentPlugin::DocumentPlugin(const score::DocumentContext& ctx, QObject* paren
9191
connect(
9292
this, &DocumentPlugin::finished, this, &DocumentPlugin::on_finished,
9393
Qt::DirectConnection);
94+
95+
auto& cstack = ctx.document.commandStack();
96+
97+
connect(
98+
&cstack, &score::CommandStack::beginTransaction, this,
99+
[this] {
100+
qDebug("Begin transaction");
101+
102+
if(m_ctxData)
103+
{
104+
SCORE_ASSERT(!m_ctxData->currentTransaction);
105+
m_ctxData->currentTransaction
106+
= std::make_shared<Execution::Transaction>(m_ctxData->context);
107+
}
108+
},
109+
Qt::DirectConnection);
110+
111+
connect(
112+
&cstack, &score::CommandStack::endTransaction, this,
113+
[this] {
114+
qDebug("Submit transaction");
115+
if(m_ctxData)
116+
{
117+
m_ctxData->currentTransaction->run_all();
118+
m_ctxData->currentTransaction.reset();
119+
}
120+
},
121+
Qt::DirectConnection);
94122
}
95123

96124
void DocumentPlugin::recreateBase()

0 commit comments

Comments
 (0)