-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Interact workflow
In MuseScore 3, most of the interactions between the user and the model are done through commands.
There is a list of shortcuts
, actions
are created from them (QActions
), the action contains a command, when the action is triggered, the command pass to the main window (MuseScore
), some commands are processed here, then pass to the current view (ScoreView
), some commands are processed into the view, then pass to the model (libmscore
), and there the rest of the teams are being processed.
In MuseScore 4, the main window (AppShell) defines only the UI structure of the application and does not contain any action handlers. Another feature, in MuseScore 4 there may be a model object (notation/libmscore), but the notation (score) view may not be loaded, for example, if we are working in the sequencer mode now, so the notation (score) cannot process actions, or several handlers of the same action may appear in different places, which leads to complexity and code duplication. For these reasons, in MuseScore 4, another workflow interaction
This architecture is very similar to Flux architecture
Example of sending and processing actions
Submit action
void NotationToolBarModel::click(const QString& action)
{
dispatcher()->dispatch(actions::namefromQString(action));
}
Register handlers
NotationActionController::NotationActionController()
{
dispatcher()->reg("note-input", this, &NotationActionController::toggleNoteInput);
dispatcher()->reg("pad-note-4", [this]() { padNote(Pad::NOTE4); });
...
}
Handlers
void NotationActionController::toggleNoteInput()
{
auto notation = currentNotation();
if (!notation) {
return;
}
if (notation->inputState()->isNoteEnterMode()) {
notation->endNoteEntry();
} else {
notation->startNoteEntry();
}
}
void NotationActionController::padNote(const Pad& pad)
{
auto notation = currentNotation();
if (!notation) {
return;
}
notation->padNote(pad);
}
Send notifications
void Notation::startNoteEntry()
{
...
m_inputStateChanged.notify();
}
void Notation::endNoteEntry()
{
...
m_inputStateChanged.notify();
}
void Notation::padNote(const Pad& pad)
{
...
m_inputStateChanged.notify();
}
Subscription and notification processing
void NotationToolBarModel::onNotationChanged()
{
std::shared_ptr<INotation> notation = globalContext()->currentNotation();
//! NOTE Unsubscribe from previous notation, if it was
m_notationChanged.resetOnNotify(this);
m_inputStateChanged.resetOnNotify(this);
if (notation) {
m_inputStateChanged = notation->inputStateChanged();
m_inputStateChanged.onNotify(this, [this]() {
updateState();
});
}
updateState();
}
void NotationPaintView::open()
{
...
m_notation->inputStateChanged().onNotify(this, [this]() {
onInputStateChanged();
});
}
Testing
- Manual testing
- Automatic testing
Translation
Compilation
- Set up developer environment
- Install Qt and Qt Creator
- Get MuseScore's source code
- Install dependencies
- Compile on the command line
- Compile in Qt Creator
Beyond compiling
Misc. development
Architecture general
- Architecture overview
- AppShell
- Modularity
- Interact workflow
- Channels and Notifications
- Settings and Configuration
- Error handling
- Launcher and Interactive
- Keyboard Navigation
Audio
Engraving
- Style settings
- Working with style files
- Style parameter changes for 4.0
- Style parameter changes for 4.1
- Style parameter changes for 4.2
- Style parameter changes for 4.3
- Style parameter changes for 4.4
Extensions
- Extensions overview
- Manifest
- Forms
- Macros
- Api
- Legacy plugin API
Google Summer of Code
References