Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

core: Implement a command transaction system #1604

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,7 @@ set(SRCS
"${CMAKE_CURRENT_SOURCE_DIR}/score/command/CommandGeneratorMap.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/score/command/PropertyCommand.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/score/command/Validity/ValidityChecker.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/score/command/Dispatchers/SendStrategy.cpp"

"${CMAKE_CURRENT_SOURCE_DIR}/score/model/Component.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/score/model/ColorInterpolator.cpp"
Expand Down
15 changes: 13 additions & 2 deletions src/lib/core/command/CommandStack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ void CommandStack::setIndex(int index)

void CommandStack::undoQuiet()
{
CommandTransaction t{*this};
updateStack([&]() {
auto cmd = m_undoable.pop();
cmd->undo(m_ctx);
Expand All @@ -131,6 +132,7 @@ void CommandStack::undoQuiet()

void CommandStack::redoQuiet()
{
CommandTransaction t{*this};
updateStack([&]() {
auto cmd = m_redoable.pop();
cmd->redo(m_ctx);
Expand Down Expand Up @@ -212,12 +214,17 @@ void CommandStack::validateDocument() const
m_checker();
}

CommandStackFacade::CommandStackFacade(CommandStack& stack)
CommandTransaction CommandStack::transaction()
{
return CommandTransaction{*this};
}

CommandStackFacade::CommandStackFacade(CommandStack& stack) noexcept
: m_stack{stack}
{
}

const DocumentContext& CommandStackFacade::context() const
const DocumentContext& CommandStackFacade::context() const noexcept
{
return m_stack.context();
}
Expand All @@ -242,4 +249,8 @@ void CommandStackFacade::enableActions() const
m_stack.enableActions();
}

CommandTransaction CommandStackFacade::transaction() const
{
return m_stack.transaction();
}
}
39 changes: 38 additions & 1 deletion src/lib/core/command/CommandStack.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
namespace score
{
class Document;

struct CommandTransaction;
/**
* \class score::CommandStack
*
Expand Down Expand Up @@ -123,6 +123,9 @@ class SCORE_LIB_BASE_EXPORT CommandStack final : public QObject
void redoQuiet();
W_INVOKABLE(redoQuiet)

void beginTransaction() E_SIGNAL(SCORE_LIB_BASE_EXPORT, beginTransaction)
void endTransaction() E_SIGNAL(SCORE_LIB_BASE_EXPORT, endTransaction)

/**
* @brief push Pushes a command on the stack
* @param cmd The command
Expand Down Expand Up @@ -205,14 +208,48 @@ class SCORE_LIB_BASE_EXPORT CommandStack final : public QObject

void validateDocument() const;

CommandTransaction transaction();

private:
friend struct CommandTransaction;
QStack<score::Command*> m_undoable;
QStack<score::Command*> m_redoable;

int m_savedIndex{};
int m_inTransaction{};

DocumentValidator m_checker;
const score::DocumentContext& m_ctx;
};

struct CommandTransaction
{
CommandStack& self;

explicit CommandTransaction(CommandStack& self)
: self{self}
{
if(self.m_inTransaction == 0)
{
self.beginTransaction();
}
self.m_inTransaction++;
}
CommandTransaction(const CommandTransaction& other) = delete;
CommandTransaction(CommandTransaction&& other) = delete;
CommandTransaction& operator=(const CommandTransaction& other) = delete;
CommandTransaction& operator=(CommandTransaction&& other) = delete;

~CommandTransaction()
{
SCORE_ASSERT(self.m_inTransaction > 0);

self.m_inTransaction--;
if(self.m_inTransaction == 0)
{
self.endTransaction();
}
}
};
}
W_REGISTER_ARGTYPE(score::Command*)
4 changes: 4 additions & 0 deletions src/lib/core/document/DocumentBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <QDir>
#include <QObject>
#include <QString>
#include <QCoreApplication>

#include <stdexcept>

Expand Down Expand Up @@ -192,7 +193,10 @@ Document* DocumentBuilder::restoreDocument(
ctx.components, writer, doc->commandStack(), [doc](score::Command* cmd) {
try
{
qDebug() << ".. replaying: " << cmd->key().toString().c_str()
<< cmd->description();
cmd->redo(doc->context());
QCoreApplication::instance()->processEvents();
return true;
}
catch(...)
Expand Down
7 changes: 5 additions & 2 deletions src/lib/score/command/CommandStackFacade.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ namespace score
{
class Command;
class CommandStack;
struct CommandTransaction;
struct DocumentContext;

/**
Expand All @@ -22,14 +23,16 @@ class SCORE_LIB_BASE_EXPORT CommandStackFacade
score::CommandStack& m_stack;

public:
explicit CommandStackFacade(score::CommandStack& stack);
explicit CommandStackFacade(score::CommandStack& stack) noexcept;

const score::DocumentContext& context() const;
const score::DocumentContext& context() const noexcept;

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

void disableActions() const;
void enableActions() const;

CommandTransaction transaction() const;
};
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#pragma once
#include <score/command/CommandStackFacade.hpp>
#include <score/command/Dispatchers/ICommandDispatcher.hpp>
#include <score/command/Dispatchers/SendStrategy.hpp>
#include <score/plugins/StringFactoryKey.hpp>
Expand Down
40 changes: 40 additions & 0 deletions src/lib/score/command/Dispatchers/SendStrategy.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#include "SendStrategy.hpp"

#include <score/command/CommandStackFacade.hpp>
#include <score/document/DocumentContext.hpp>

#include <core/command/CommandStack.hpp>

namespace SendStrategy
{

void Simple::send(const score::CommandStackFacade& stack, score::Command* other)
{
auto trans = stack.context().commandStack.transaction();
stack.redoAndPush(other);
}

void UndoRedo::send(const score::CommandStackFacade& stack, score::Command* other)
{
auto trans = stack.context().commandStack.transaction();
other->undo(stack.context());
stack.redoAndPush(other);
}

void Quiet::send(const score::CommandStackFacade& stack, score::Command* other)
{
stack.push(other);
}

}
namespace RedoStrategy
{
void Redo::redo(const score::DocumentContext& ctx, score::Command& cmd)
{
auto trans = ctx.commandStack.transaction();
cmd.redo(ctx);
}

void Quiet::redo(const score::DocumentContext& ctx, score::Command& cmd) { }

}
41 changes: 17 additions & 24 deletions src/lib/score/command/Dispatchers/SendStrategy.hpp
Original file line number Diff line number Diff line change
@@ -1,45 +1,38 @@
#pragma once
#include <score/command/CommandStackFacade.hpp>
#include <score_lib_base_export.h>
namespace score
{
class CommandStackFacade;
class Command;
struct DocumentContext;
}

namespace SendStrategy
{
struct Simple
struct SCORE_LIB_BASE_EXPORT Simple
{
static void send(const score::CommandStackFacade& stack, score::Command* other)
{
stack.redoAndPush(other);
}
static void send(const score::CommandStackFacade& stack, score::Command* other);
};

struct Quiet
struct SCORE_LIB_BASE_EXPORT Quiet
{
static void send(const score::CommandStackFacade& stack, score::Command* other)
{
stack.push(other);
}
static void send(const score::CommandStackFacade& stack, score::Command* other);
};

struct UndoRedo
struct SCORE_LIB_BASE_EXPORT UndoRedo
{
static void send(const score::CommandStackFacade& stack, score::Command* other)
{
other->undo(stack.context());
stack.redoAndPush(other);
}
static void send(const score::CommandStackFacade& stack, score::Command* other);
};
}
namespace RedoStrategy
{
struct Redo
struct SCORE_LIB_BASE_EXPORT Redo
{
static void redo(const score::DocumentContext& ctx, score::Command& cmd)
{
cmd.redo(ctx);
}
static void redo(const score::DocumentContext& ctx, score::Command& cmd);
};

struct Quiet
struct SCORE_LIB_BASE_EXPORT Quiet
{
static void redo(const score::DocumentContext& ctx, score::Command& cmd) { }
static void redo(const score::DocumentContext& ctx, score::Command& cmd);
};
}
1 change: 1 addition & 0 deletions src/plugins/score-lib-process/Effect/EffectLayout.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <cmath>

#include <cstdint>
// FIXME put the entirety of this as dynamic behaviour instead
namespace Process
{

Expand Down
4 changes: 4 additions & 0 deletions src/plugins/score-lib-process/Process/ExecutionContext.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ struct SCORE_LIB_PROCESS_EXPORT Context
const std::shared_ptr<ossia::graph_interface>& execGraph;
const std::shared_ptr<ossia::execution_state>& execState;

std::shared_ptr<Execution::Transaction>& transaction;

void execCommand(ExecutionCommand&& cmd);

auto& context() const { return *this; }

#if !defined(_MSC_VER)
Expand Down
17 changes: 12 additions & 5 deletions src/plugins/score-lib-process/Process/ExecutionSetup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,19 @@

namespace Execution
{
void Context::execCommand(ExecutionCommand&& cmd)
{
if(transaction)
transaction->push_back(std::move(cmd));
else
executionQueue.enqueue(std::move(cmd));
}

static auto enqueue_in_context(SetupContext& self) noexcept
{
return [&self]<typename F>(F&& f) {
static_assert(std::is_nothrow_move_constructible_v<F>);
self.context.executionQueue.enqueue(std::move(f));
self.context.execCommand(std::move(f));
};
}

Expand Down Expand Up @@ -72,7 +79,7 @@ void SetupContext::on_cableRemoved(const Process::Cable& c)
auto it = m_cables.find(c.id());
if(it != m_cables.end())
{
context.executionQueue.enqueue(
context.execCommand(
[cable = it->second, graph = context.execGraph] { graph->disconnect(cable); });
}
}
Expand Down Expand Up @@ -135,7 +142,7 @@ void SetupContext::connectCable(Process::Cable& cable)
}

m_cables[cable.id()] = edge;
context.executionQueue.enqueue([edge, graph = context.execGraph]() mutable {
context.execCommand([edge, graph = context.execGraph]() mutable {
graph->connect(std::move(edge));
});
}
Expand Down Expand Up @@ -428,7 +435,7 @@ void SetupContext::unregister_inlet(
if(ossia_port_it != inlets.end())
{
std::weak_ptr<ossia::execution_state> ws = context.execState;
context.executionQueue.enqueue([ws, ossia_port = ossia_port_it->second.second] {
context.execCommand([ws, ossia_port = ossia_port_it->second.second] {
if(auto state = ws.lock())
state->unregister_port(*ossia_port);
});
Expand Down Expand Up @@ -533,7 +540,7 @@ void SetupContext::unregister_node(
{
std::weak_ptr<ossia::graph_interface> wg = context.execGraph;
std::weak_ptr<ossia::execution_state> ws = context.execState;
context.executionQueue.enqueue([wg, ws, node] {
context.execCommand([wg, ws, node] {
if(auto s = ws.lock())
{
ossia::for_each_inlet(*node, [&](auto& p) { s->unregister_port(p); });
Expand Down
30 changes: 29 additions & 1 deletion src/plugins/score-plugin-engine/Execution/DocumentPlugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ DocumentPlugin::ContextData::ContextData(const score::DocumentContext& ctx)
, context
{
{}, ctx, m_created, {}, {}, m_execQueue, m_editionQueue, m_gcQueue, setupContext,
execGraph, execState
execGraph, execState, currentTransaction
#if(__cplusplus > 201703L) && !defined(_MSC_VER)
,
{
Expand Down Expand Up @@ -91,6 +91,34 @@ DocumentPlugin::DocumentPlugin(const score::DocumentContext& ctx, QObject* paren
connect(
this, &DocumentPlugin::finished, this, &DocumentPlugin::on_finished,
Qt::DirectConnection);

auto& cstack = ctx.document.commandStack();

connect(
&cstack, &score::CommandStack::beginTransaction, this,
[this] {
qDebug("Begin transaction");

if(m_ctxData)
{
SCORE_ASSERT(!m_ctxData->currentTransaction);
m_ctxData->currentTransaction
= std::make_shared<Execution::Transaction>(m_ctxData->context);
}
},
Qt::DirectConnection);

connect(
&cstack, &score::CommandStack::endTransaction, this,
[this] {
qDebug("Submit transaction");
if(m_ctxData)
{
m_ctxData->currentTransaction->run_all();
m_ctxData->currentTransaction.reset();
}
},
Qt::DirectConnection);
}

void DocumentPlugin::recreateBase()
Expand Down
Loading
Loading