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

Plugin API: merge changes from onScoreStateChanged with user action in undo stack #5626

Merged
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
2 changes: 1 addition & 1 deletion libmscore/score.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -818,7 +818,7 @@ bool Score::dirty() const

ScoreContentState Score::state() const
{
return std::make_pair(this, undoStack()->state());
return ScoreContentState(this, undoStack()->state());
}

//---------------------------------------------------------
Expand Down
13 changes: 12 additions & 1 deletion libmscore/score.h
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,18 @@ class UpdateState {
// ScoreContentState
//---------------------------------------------------------

typedef std::pair<const Score*, int> ScoreContentState;
class ScoreContentState {
const Score* score;
int num;
public:
ScoreContentState() : score(nullptr), num(0) {}
ScoreContentState(const Score* s, int stateNum) : score(s), num(stateNum) {}

bool operator==(const ScoreContentState& s2) const { return score == s2.score && num == s2.num; }
bool operator!=(const ScoreContentState& s2) const { return !(*this == s2); }

bool isNewerThan(const ScoreContentState& s2) const { return score == s2.score && num > s2.num; }
};

class MasterScore;

Expand Down
6 changes: 3 additions & 3 deletions mscore/musescore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4832,7 +4832,7 @@ void MuseScore::undoRedo(bool undo)
cv->changeState(ViewState::NORMAL);
cv->startUndoRedo(undo);
updateInputState(cs);
endCmd();
endCmd(/* undoRedo */ true);
if (pianorollEditor)
pianorollEditor->update();
}
Expand Down Expand Up @@ -5813,10 +5813,10 @@ void MuseScore::cmd(QAction* a)
// Updates the UI after a possible score change.
//---------------------------------------------------------

void MuseScore::endCmd()
void MuseScore::endCmd(bool undoRedo)
{
#ifdef SCRIPT_INTERFACE
getPluginEngine()->beginEndCmd(this);
getPluginEngine()->beginEndCmd(this, undoRedo);
#endif
if (timeline())
timeline()->updateGrid();
Expand Down
3 changes: 2 additions & 1 deletion mscore/musescore.h
Original file line number Diff line number Diff line change
Expand Up @@ -697,7 +697,8 @@ class MuseScore : public QMainWindow, public MuseScoreCore {

Q_INVOKABLE void openExternalLink(const QString&);

virtual void endCmd() override;
void endCmd(bool undoRedo);
void endCmd() override { endCmd(false); };
void printFile();
void exportFile();
bool exportParts();
Expand Down
22 changes: 22 additions & 0 deletions mscore/plugin/api/score.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
#include "libmscore/segment.h"
#include "libmscore/text.h"

#include "musescore.h"
#include "../qmlpluginengine.h"

namespace Ms {
namespace PluginAPI {

Expand Down Expand Up @@ -118,5 +121,24 @@ Measure* Score::lastMeasureMM()
return wrap<Measure>(score()->lastMeasureMM(), Ownership::SCORE);
}

//---------------------------------------------------------
// Score::startCmd
//---------------------------------------------------------

void Score::startCmd()
{
// TODO: should better use qmlEngine(this) (need to set context for wrappers then)
const QmlPluginEngine* engine = mscore->getPluginEngine();
if (engine->inScoreChangeActionHandler()) {
// Plugin-originated changes made while handling onScoreStateChanged
// should be grouped together with the action which caused this change
// (if it was caused by actual score change).
if (!score()->undoStack()->active())
score()->undoStack()->reopen();
}
else {
score()->startCmd();
}
}
}
}
2 changes: 1 addition & 1 deletion mscore/plugin/api/score.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ class Score : public Ms::PluginAPI::ScoreElement {
* least once by "dock" type plugins in case they
* modify the score.
*/
Q_INVOKABLE void startCmd() { score()->startCmd(); }
Q_INVOKABLE void startCmd();
/**
* For "dock" type plugins: to be used after score
* modifications to make them undoable.
Expand Down
30 changes: 26 additions & 4 deletions mscore/plugin/qmlpluginengine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,22 +40,28 @@ QmlPluginEngine::QmlPluginEngine(QObject* parent)
// QmlPluginEngine::beginEndCmd
//---------------------------------------------------------

void QmlPluginEngine::beginEndCmd(MuseScore* ms)
void QmlPluginEngine::beginEndCmd(MuseScore* ms, bool inUndoRedo)
{
++cmdCount;

if (inUndoRedo)
undoRedo = true;

const Score* cs = ms->currentScore();
currScoreState = cs->masterScore()->state(); // score and excerpts have united undo stack so we are better to track master score

// TODO: most of plugins are never deleted so receivers usually never decrease
if (!receivers(SIGNAL(endCmd(const QMap<QString, QVariant>&))))
return;

const Score* cs = ms->currentScore();

endCmdInfo["selectionChanged"] = !cs || cs->selectionChanged();
endCmdInfo["excerptsChanged"] = !cs || cs->masterScore()->excerptsChanged();
endCmdInfo["instrumentsChanged"] = !cs || cs->masterScore()->instrumentsChanged();

endCmdInfo["startLayoutTick"] = cs ? cs->cmdState().startTick().ticks() : -1;
endCmdInfo["endLayoutTick"] = cs ? cs->cmdState().endTick().ticks() : -1;

endCmdInfo["undoRedo"] = undoRedo;
}

//---------------------------------------------------------
Expand All @@ -73,7 +79,23 @@ void QmlPluginEngine::endEndCmd(MuseScore*)
emit endCmd(endCmdInfo);

--cmdCount;
if (!cmdCount)
if (!cmdCount) {
recursion = false;
undoRedo = false;
lastScoreState = currScoreState;
}
}

//---------------------------------------------------------
// QmlPluginEngine::inScoreChangeActionHandler
/// Returns \p true if the engine is in process of
/// handling endCmd() call which is a result of score
/// change user action (not undo/redo or simple selection
/// changes/mouse clicks etc.)
//---------------------------------------------------------

bool QmlPluginEngine::inScoreChangeActionHandler() const
{
return cmdCount > 0 && !undoRedo && currScoreState.isNewerThan(lastScoreState);
}
}
9 changes: 8 additions & 1 deletion mscore/plugin/qmlpluginengine.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#define __QMLPLUGINENGINE_H__

#include "../qml/msqmlengine.h"
#include "libmscore/score.h"

namespace Ms {

Expand All @@ -36,14 +37,20 @@ class QmlPluginEngine : public MsQmlEngine {
QMap<QString, QVariant> endCmdInfo;
int cmdCount = 0;
bool recursion = false;
bool undoRedo = false;

ScoreContentState lastScoreState;
ScoreContentState currScoreState;

signals:
void endCmd(const QMap<QString, QVariant>& changes);
public:
QmlPluginEngine(QObject* parent = nullptr);

void beginEndCmd(MuseScore*);
void beginEndCmd(MuseScore*, bool undoRedo);
void endEndCmd(MuseScore*);

bool inScoreChangeActionHandler() const;
};

} // namespace Ms
Expand Down