diff --git a/CMakeLists.txt b/CMakeLists.txt index a437b912b0..e07f99206f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,6 +82,8 @@ option(ENGRAVING_COMPAT_WRITESTYLE_302 "Write style to score xml file" ON) option(ENGRAVING_COMPAT_WRITEEXCERPTS_302 "Write excerpts to score xml file" ON) # ----- +option(UI_DISABLE_MODALITY "Disable dialogs modality for testing purpose" OFF) + option(ACCESSIBILITY_LOGGING_ENABLED "Enable accessibility logging" OFF) option(SOUNDFONT3 "Ogg Vorbis compressed fonts" ON) # Enable Ogg Vorbis compressed fonts, requires Ogg & Vorbis diff --git a/build/config.h.in b/build/config.h.in index 174e4b5075..269b3ba44c 100644 --- a/build/config.h.in +++ b/build/config.h.in @@ -72,6 +72,8 @@ #cmakedefine ENGRAVING_COMPAT_WRITESTYLE_302 #cmakedefine ENGRAVING_COMPAT_WRITEEXCERPTS_302 +#cmakedefine UI_DISABLE_MODALITY + #cmakedefine ACCESSIBILITY_LOGGING_ENABLED #cmakedefine SPARKLE_ENABLED diff --git a/src/appshell/view/appmenumodel.cpp b/src/appshell/view/appmenumodel.cpp index 02414c765b..055035aa06 100644 --- a/src/appshell/view/appmenumodel.cpp +++ b/src/appshell/view/appmenumodel.cpp @@ -330,8 +330,8 @@ MenuItem AppMenuModel::diagnosticItem() const }; MenuItemList autobotItems { - makeMenuItem("autobot-show-batchtests"), makeMenuItem("autobot-show-scripts"), + makeMenuItem("autobot-show-batchtests"), }; MenuItemList items { diff --git a/src/autobot/iautobotconfiguration.h b/src/autobot/iautobotconfiguration.h index 907b646fec..08f125f7a3 100644 --- a/src/autobot/iautobotconfiguration.h +++ b/src/autobot/iautobotconfiguration.h @@ -36,7 +36,8 @@ public: virtual io::path dataPath() const = 0; - virtual io::path filesPath() const = 0; + virtual io::path testingFilesPath() const = 0; + virtual io::path savingFilesPath() const = 0; virtual io::path drawDataPath() const = 0; virtual io::path fileDrawDataPath(const io::path& filePath) const = 0; diff --git a/src/autobot/internal/api/autobotapi.cpp b/src/autobot/internal/api/autobotapi.cpp index 0e5d8e8ef1..d5a1c5c509 100644 --- a/src/autobot/internal/api/autobotapi.cpp +++ b/src/autobot/internal/api/autobotapi.cpp @@ -22,6 +22,9 @@ #include "autobotapi.h" #include +#include +#include +#include #include "async/async.h" @@ -36,12 +39,23 @@ AutobotApi::AutobotApi(IApiEngine* e) bool AutobotApi::openProject(const QString& name) { - io::path dir = autobotConfiguration()->filesPath(); + io::path dir = autobotConfiguration()->testingFilesPath(); io::path filePath = dir + "/" + name; Ret ret = projectFilesController()->openProject(filePath); return ret; } +void AutobotApi::saveProject(const QString& name) +{ + io::path dir = autobotConfiguration()->savingFilesPath(); + if (!QFileInfo::exists(dir.toQString())) { + QDir().mkpath(dir.toQString()); + } + + io::path filePath = dir + "/" + QDateTime::currentDateTime().toString(Qt::ISODate) + "_" + name; + projectFilesController()->saveProject(filePath); +} + void AutobotApi::setInterval(int msec) { m_intervalMsec = msec; @@ -109,7 +123,7 @@ bool AutobotApi::pause() return true; } -void AutobotApi::sleep(int msec) +void AutobotApi::sleep(int msec) const { if (msec < 0) { msec = m_intervalMsec; @@ -122,12 +136,17 @@ void AutobotApi::sleep(int msec) loop.exec(); } -void AutobotApi::waitPopup() +void AutobotApi::waitPopup() const { //! NOTE We could do it smartly, check a current popup actually opened, but or just sleep some time sleep(500); } +void AutobotApi::seeChanges(int msec) +{ + sleep(msec); +} + void AutobotApi::async(const QJSValue& func, const QJSValueList& args) { async::Async::call(this, [func, args]() { @@ -135,3 +154,10 @@ void AutobotApi::async(const QJSValue& func, const QJSValueList& args) mut_func.call(args); }); } + +int AutobotApi::randomInt(int min, int max) const +{ + srand(time(nullptr)); // Seed the time + int val = rand() % (max - min + 1) + min; + return val; +} diff --git a/src/autobot/internal/api/autobotapi.h b/src/autobot/internal/api/autobotapi.h index 400bd37ae4..487bbe6e50 100644 --- a/src/autobot/internal/api/autobotapi.h +++ b/src/autobot/internal/api/autobotapi.h @@ -49,12 +49,15 @@ public: Q_INVOKABLE void runTestCase(const QJSValue& testCase); Q_INVOKABLE bool openProject(const QString& name); + Q_INVOKABLE void saveProject(const QString& name = QString()); Q_INVOKABLE void abort(); Q_INVOKABLE bool pause(); - Q_INVOKABLE void sleep(int msec = -1); - Q_INVOKABLE void waitPopup(); + Q_INVOKABLE void sleep(int msec = -1) const; + Q_INVOKABLE void waitPopup() const; + Q_INVOKABLE void seeChanges(int msec = 300); Q_INVOKABLE void async(const QJSValue& func, const QJSValueList& args = QJSValueList()); + Q_INVOKABLE int randomInt(int min, int max) const; private: diff --git a/src/autobot/internal/api/navigationapi.cpp b/src/autobot/internal/api/navigationapi.cpp index a51c152e28..7132355039 100644 --- a/src/autobot/internal/api/navigationapi.cpp +++ b/src/autobot/internal/api/navigationapi.cpp @@ -24,10 +24,19 @@ #include "log.h" using namespace mu::api; +using namespace mu::ui; NavigationApi::NavigationApi(IApiEngine* e) : ApiObject(e) { + //! NOTE Disable reset on mouse press for testing purpose + navigation()->setIsResetOnMousePress(false); +} + +NavigationApi::~NavigationApi() +{ + //! NOTE Return default + navigation()->setIsResetOnMousePress(true); } void NavigationApi::nextPanel() @@ -60,6 +69,11 @@ void NavigationApi::down() dispatcher()->dispatch("nav-down"); } +void NavigationApi::escape() +{ + dispatcher()->dispatch("nav-escape"); +} + bool NavigationApi::goToControl(const QString& section, const QString& panel, const QString& contol) { bool ok = navigation()->requestActivateByName(section.toStdString(), panel.toStdString(), contol.toStdString()); @@ -79,3 +93,21 @@ bool NavigationApi::triggerControl(const QString& section, const QString& panel, } return ok; } + +QString NavigationApi::activeSection() const +{ + INavigationSection* sec = navigation()->activeSection(); + return sec ? sec->name() : QString(); +} + +QString NavigationApi::activePanel() const +{ + INavigationPanel* p = navigation()->activePanel(); + return p ? p->name() : QString(); +} + +QString NavigationApi::activeControl() const +{ + INavigationControl* c = navigation()->activeControl(); + return c ? c->name() : QString(); +} diff --git a/src/autobot/internal/api/navigationapi.h b/src/autobot/internal/api/navigationapi.h index 0dedd46af2..93ddf1e552 100644 --- a/src/autobot/internal/api/navigationapi.h +++ b/src/autobot/internal/api/navigationapi.h @@ -39,6 +39,7 @@ class NavigationApi : public ApiObject public: explicit NavigationApi(IApiEngine* e); + ~NavigationApi(); Q_INVOKABLE void nextPanel(); Q_INVOKABLE void prevPanel(); @@ -46,9 +47,14 @@ public: Q_INVOKABLE void left(); Q_INVOKABLE void up(); Q_INVOKABLE void down(); + Q_INVOKABLE void escape(); Q_INVOKABLE bool goToControl(const QString& section, const QString& panel, const QString& contol); Q_INVOKABLE void trigger(); Q_INVOKABLE bool triggerControl(const QString& section, const QString& panel, const QString& contol); + + Q_INVOKABLE QString activeSection() const; + Q_INVOKABLE QString activePanel() const; + Q_INVOKABLE QString activeControl() const; }; } diff --git a/src/autobot/internal/autobot.cpp b/src/autobot/internal/autobot.cpp index 95f214f9e8..e4058fbf11 100644 --- a/src/autobot/internal/autobot.cpp +++ b/src/autobot/internal/autobot.cpp @@ -113,7 +113,7 @@ mu::RetVal Autobot::filesList() const { using namespace mu::system; - io::path filesPath = configuration()->filesPath(); + io::path filesPath = configuration()->testingFilesPath(); LOGI() << "filesPath: " << filesPath; RetVal paths = fileSystem()->scanFiles(filesPath, QStringList(), IFileSystem::ScanMode::OnlyCurrentDir); return paths; diff --git a/src/autobot/internal/autobotconfiguration.cpp b/src/autobot/internal/autobotconfiguration.cpp index c9719a08f4..2b52eabc42 100644 --- a/src/autobot/internal/autobotconfiguration.cpp +++ b/src/autobot/internal/autobotconfiguration.cpp @@ -22,24 +22,36 @@ #include "autobotconfiguration.h" #include +#include using namespace mu::autobot; bool AutobotConfiguration::isConfigured() const { - return !dataPath().empty() && !filesPath().empty(); + return !dataPath().empty() && !testingFilesPath().empty(); } mu::io::path AutobotConfiguration::dataPath() const { - return io::path(std::getenv("MU_AUTOBOT_DATA_PATH")); + io::path p = io::path(std::getenv("MU_AUTOBOT_DATA_PATH")); + if (p.empty()) { + QDir projDir(QString(PROJECT_ROOT_DIR)); + projDir.cdUp(); + p = projDir.absolutePath() + "/mu_autobot_data"; + } + return p; } -mu::io::path AutobotConfiguration::filesPath() const +mu::io::path AutobotConfiguration::testingFilesPath() const { return io::path(std::getenv("MU_AUTOBOT_FILES_PATH")); } +mu::io::path AutobotConfiguration::savingFilesPath() const +{ + return dataPath() + "/saving_files"; +} + mu::io::path AutobotConfiguration::drawDataPath() const { return dataPath() + "/draw_data"; diff --git a/src/autobot/internal/autobotconfiguration.h b/src/autobot/internal/autobotconfiguration.h index db142dd046..00a1ccca60 100644 --- a/src/autobot/internal/autobotconfiguration.h +++ b/src/autobot/internal/autobotconfiguration.h @@ -34,7 +34,8 @@ public: io::path dataPath() const override; - io::path filesPath() const override; + io::path testingFilesPath() const override; + io::path savingFilesPath() const override; io::path drawDataPath() const override; io::path fileDrawDataPath(const io::path& filePath) const override; diff --git a/src/autobot/qml/MuseScore/Autobot/ScriptsDialog.qml b/src/autobot/qml/MuseScore/Autobot/ScriptsDialog.qml index 7dfbe909c9..1fe82d0ddd 100644 --- a/src/autobot/qml/MuseScore/Autobot/ScriptsDialog.qml +++ b/src/autobot/qml/MuseScore/Autobot/ScriptsDialog.qml @@ -28,8 +28,8 @@ StyledDialogView { title: "Autobot" - contentHeight: 800 - contentWidth: 600 + contentHeight: 600 + contentWidth: 400 resizable: true ScriptsPanel { diff --git a/src/framework/ui/inavigationcontroller.h b/src/framework/ui/inavigationcontroller.h index a4f7c62ab0..3fa36958df 100644 --- a/src/framework/ui/inavigationcontroller.h +++ b/src/framework/ui/inavigationcontroller.h @@ -46,6 +46,8 @@ public: virtual INavigationControl* activeControl() const = 0; virtual async::Notification navigationChanged() const = 0; + + virtual void setIsResetOnMousePress(bool arg) = 0; }; } diff --git a/src/framework/ui/internal/navigationcontroller.cpp b/src/framework/ui/internal/navigationcontroller.cpp index e5326559f7..fa01e5ca86 100644 --- a/src/framework/ui/internal/navigationcontroller.cpp +++ b/src/framework/ui/internal/navigationcontroller.cpp @@ -315,13 +315,30 @@ const std::set& NavigationController::sections() const return m_sections; } +void NavigationController::setIsResetOnMousePress(bool arg) +{ + m_isResetOnMousePress = arg; +} + +void NavigationController::resetActiveIfNeed(QObject* watched) +{ + if (!m_isResetOnMousePress) { + return; + } + +#ifdef BUILD_DIAGNOSTICS + if (diagnostics::isDiagnosticHierarchy(watched)) { + return; + } +#endif + + resetActive(); +} + bool NavigationController::eventFilter(QObject* watched, QEvent* event) { if (event->type() == QEvent::MouseButtonPress) { -#ifdef BUILD_DIAGNOSTICS - if (!diagnostics::isDiagnosticHierarchy(watched)) -#endif - resetActive(); + resetActiveIfNeed(watched); } return QObject::eventFilter(watched, event); @@ -420,7 +437,12 @@ void NavigationController::doActivatePanel(INavigationPanel* panel) ctrlIndex.row = idxVal.at(0).toInt(); ctrlIndex.column = idxVal.at(1).toInt(); control = findByIndex(panel->controls(), ctrlIndex); - IF_ASSERT_FAILED(control) { + if (!control) { + bool isOptional = event->data.value("controlOptional").toBool(); + if (!isOptional) { + IF_ASSERT_FAILED(control) { + } + } } } } diff --git a/src/framework/ui/internal/navigationcontroller.h b/src/framework/ui/internal/navigationcontroller.h index 36fb35ab3d..9ce813dd5b 100644 --- a/src/framework/ui/internal/navigationcontroller.h +++ b/src/framework/ui/internal/navigationcontroller.h @@ -63,6 +63,8 @@ public: async::Notification navigationChanged() const override; + void setIsResetOnMousePress(bool arg) override; + void init(); private: @@ -100,10 +102,12 @@ private: void doActivateFirst(); void doActivateLast(); + void resetActiveIfNeed(QObject* watched); void resetActive(); std::set m_sections; async::Notification m_navigationChanged; + bool m_isResetOnMousePress = true; }; } diff --git a/src/framework/uicomponents/view/popupview.cpp b/src/framework/uicomponents/view/popupview.cpp index 667e8aa574..6ae04bd17a 100644 --- a/src/framework/uicomponents/view/popupview.cpp +++ b/src/framework/uicomponents/view/popupview.cpp @@ -36,6 +36,7 @@ #include "popupwindow/popupwindow_qquickview.h" #include "log.h" +#include "config.h" using namespace mu::uicomponents; @@ -161,6 +162,9 @@ void PopupView::open() } qWindow->setTitle(m_title); qWindow->setModality(m_modal ? Qt::ApplicationModal : Qt::NonModal); +#ifdef UI_DISABLE_MODALITY + qWindow->setModality(Qt::NonModal); +#endif QRect winRect = m_window->geometry(); qWindow->setMinimumSize(winRect.size()); diff --git a/src/instrumentsscene/qml/MuseScore/InstrumentsScene/internal/InstrumentsView.qml b/src/instrumentsscene/qml/MuseScore/InstrumentsScene/internal/InstrumentsView.qml index 232bb55093..c350d27b75 100644 --- a/src/instrumentsscene/qml/MuseScore/InstrumentsScene/internal/InstrumentsView.qml +++ b/src/instrumentsscene/qml/MuseScore/InstrumentsScene/internal/InstrumentsView.qml @@ -58,6 +58,8 @@ Item { onNavigationEvent: { if (event.type === NavigationEvent.AboutActive) { event.setData("controlIndex", prv.currentItemNavigationIndex) + //! NOTE If we changed family, then control with saved index maybe not + event.setData("controlOptional", true) } } } diff --git a/src/project/internal/projectfilescontroller.cpp b/src/project/internal/projectfilescontroller.cpp index 81f5e0270f..3a78401622 100644 --- a/src/project/internal/projectfilescontroller.cpp +++ b/src/project/internal/projectfilescontroller.cpp @@ -42,9 +42,9 @@ void ProjectFilesController::init() dispatcher()->reg(this, "file-new", this, &ProjectFilesController::newProject); dispatcher()->reg(this, "file-close", [this]() { closeOpenedProject(); }); - dispatcher()->reg(this, "file-save", this, &ProjectFilesController::saveScore); - dispatcher()->reg(this, "file-save-as", this, &ProjectFilesController::saveScoreAs); - dispatcher()->reg(this, "file-save-a-copy", this, &ProjectFilesController::saveScoreCopy); + dispatcher()->reg(this, "file-save", [this]() { saveProject(); }); + dispatcher()->reg(this, "file-save-as", this, &ProjectFilesController::saveProjectAs); + dispatcher()->reg(this, "file-save-a-copy", this, &ProjectFilesController::saveProjectCopy); dispatcher()->reg(this, "file-save-selection", this, &ProjectFilesController::saveSelection); dispatcher()->reg(this, "file-save-online", this, &ProjectFilesController::saveOnline); @@ -206,7 +206,7 @@ bool ProjectFilesController::closeOpenedProject() if (btn == IInteractive::Button::Cancel) { return false; } else if (btn == IInteractive::Button::Save) { - saveScore(); + saveProject(); } } @@ -238,16 +238,21 @@ IInteractive::Button ProjectFilesController::askAboutSavingScore(const io::path& return result.standardButton(); } -void ProjectFilesController::saveScore() +void ProjectFilesController::saveProject(const io::path& path) { if (!currentNotationProject()->created().val) { doSaveScore(); return; } - io::path defaultFilePath = defaultSavingFilePath(); + io::path filePath; + if (!path.empty()) { + filePath = path; + } else { + io::path defaultFilePath = defaultSavingFilePath(); + filePath = selectScoreSavingFile(defaultFilePath, qtrc("project", "Save score")); + } - io::path filePath = selectScoreSavingFile(defaultFilePath, qtrc("project", "Save score")); if (filePath.empty()) { return; } @@ -259,7 +264,7 @@ void ProjectFilesController::saveScore() doSaveScore(filePath); } -void ProjectFilesController::saveScoreAs() +void ProjectFilesController::saveProjectAs() { io::path defaultFilePath = defaultSavingFilePath(); io::path selectedFilePath = selectScoreSavingFile(defaultFilePath, qtrc("project", "Save score")); @@ -270,7 +275,7 @@ void ProjectFilesController::saveScoreAs() doSaveScore(selectedFilePath, SaveMode::SaveAs); } -void ProjectFilesController::saveScoreCopy() +void ProjectFilesController::saveProjectCopy() { io::path defaultFilePath = defaultSavingFilePath(); io::path selectedFilePath = selectScoreSavingFile(defaultFilePath, qtrc("project", "Save a copy")); diff --git a/src/project/internal/projectfilescontroller.h b/src/project/internal/projectfilescontroller.h index 5d89e90bfe..a50df103f0 100644 --- a/src/project/internal/projectfilescontroller.h +++ b/src/project/internal/projectfilescontroller.h @@ -56,6 +56,7 @@ public: Ret openProject(const io::path& projectPath) override; bool closeOpenedProject() override; bool isProjectOpened(const io::path& scorePath) const override; + void saveProject(const io::path& path = io::path()) override; private: void setupConnections(); @@ -72,9 +73,8 @@ private: bool checkCanIgnoreError(const Ret& ret, const io::path& filePath); framework::IInteractive::Button askAboutSavingScore(const io::path& filePath); - void saveScore(); - void saveScoreAs(); - void saveScoreCopy(); + void saveProjectAs(); + void saveProjectCopy(); void saveSelection(); void saveOnline(); diff --git a/src/project/iprojectfilescontroller.h b/src/project/iprojectfilescontroller.h index 023fbac6b7..a84e654fee 100644 --- a/src/project/iprojectfilescontroller.h +++ b/src/project/iprojectfilescontroller.h @@ -34,9 +34,10 @@ class IProjectFilesController : MODULE_EXPORT_INTERFACE public: virtual ~IProjectFilesController() = default; - virtual Ret openProject(const io::path& scorePath) = 0; + virtual Ret openProject(const io::path& path) = 0; virtual bool closeOpenedProject() = 0; - virtual bool isProjectOpened(const io::path& scorePath) const = 0; + virtual bool isProjectOpened(const io::path& path) const = 0; + virtual void saveProject(const io::path& path = io::path()) = 0; }; } diff --git a/test_scripts/NewScore10InstrPutNotesSaveClose.js b/test_scripts/NewScore10InstrPutNotesSaveClose.js new file mode 100644 index 0000000000..9da7617b8f --- /dev/null +++ b/test_scripts/NewScore10InstrPutNotesSaveClose.js @@ -0,0 +1,49 @@ +var NewScore = require("steps/NewScore.js") + +function main() +{ + var testCase = { + name: "NewScore10InstrPutNotesSaveClose", + steps: [ + {name: "Open Dialog", func: function() { + NewScore.openNewScoreDialog() + }}, + {name: "Select Instruments", func: function() { + NewScore.chooseRandomInstruments(10) + }}, + {name: "Note input mode", func: function() { + + api.navigation.triggerControl("NoteInputSection", "NoteInputBar", "note-input-steptime") + api.autobot.waitPopup() + // First item become automatically current, so just trigger + api.navigation.trigger() + + // Select note + api.navigation.triggerControl("NoteInputSection", "NoteInputBar", "pad-note-8") + }}, + {name: "Note input", func: function() { + api.dispatcher.dispatch("note-c") + api.dispatcher.dispatch("note-d") + api.dispatcher.dispatch("note-e") + api.dispatcher.dispatch("note-f") + api.dispatcher.dispatch("note-g") + api.dispatcher.dispatch("note-a") + api.dispatcher.dispatch("note-b") + }}, + {name: "Save", func: function() { + api.autobot.saveProject("NewScore10InstrPutNotesSaveClose.mscz") + }}, + {name: "Close", func: function() { + api.dispatcher.dispatch("file-close") + api.autobot.seeChanges() + + // Go Home + api.navigation.triggerControl("TopTool", "MainToolBar", "Home") + }} + ] + }; + + api.autobot.setInterval(1000) + api.autobot.runTestCase(testCase) + api.log.info("----------- end script ---------------") +} diff --git a/test_scripts/steps/NewScore.js b/test_scripts/steps/NewScore.js new file mode 100644 index 0000000000..4d13a5458c --- /dev/null +++ b/test_scripts/steps/NewScore.js @@ -0,0 +1,83 @@ + +function openNewScoreDialog() +{ + api.navigation.triggerControl("AppTitleBar", "AppMenuBar", "&File") + // wait popup open + api.autobot.waitPopup() + // New become automatically current, so just trigger + api.navigation.trigger() +} + +function сhooseFluteAndPiano() +{ + // Flute + api.navigation.goToControl("NewScoreDialog", "FamilyView", "Woodwinds") + api.navigation.goToControl("NewScoreDialog", "InstrumentsView", "Flute") + api.navigation.triggerControl("NewScoreDialog", "SelectPanel", "Select") + + // just for see changes + api.autobot.sleep(500) + + // Piano + api.navigation.goToControl("NewScoreDialog", "FamilyView", "Keyboards") + api.navigation.goToControl("NewScoreDialog", "InstrumentsView", "Piano") + api.navigation.triggerControl("NewScoreDialog", "SelectPanel", "Select") + + // just for see changes + api.autobot.sleep(500) + + // Done + api.navigation.triggerControl("NewScoreDialog", "BottomPanel", "Done") +} + +function chooseRandomInstruments(count, see_msec) +{ + see_msec = see_msec || 50 + api.log.debug("chooseRandomInstruments count: " + count) + for (var i = 0; i < count; i++) { + + api.log.debug("chooseRandomInstruments i: " + i) + // Go to first family + api.navigation.goToControl("NewScoreDialog", "FamilyView", "Woodwinds") + + // Choose family + var familyCount = api.autobot.randomInt(0, 20); + api.log.debug("chooseRandomInstruments familyCount: " + familyCount) + for (var f = 0; f < familyCount; f++) { + api.navigation.down() + api.autobot.seeChanges(see_msec) + } + + if (api.navigation.activeControl() === "genreBox") { + api.navigation.down() + } + + // Got to Instruments + api.navigation.nextPanel() + api.autobot.seeChanges(see_msec) + + // Choose instrument + var instrCount = api.autobot.randomInt(0, 20); + api.log.debug("chooseRandomInstruments instrCount: " + instrCount) + for (var j = 0; j < instrCount; j++) { + api.navigation.down() + api.autobot.seeChanges(see_msec) + } + + if (api.navigation.activeControl() === "SearchInstruments") { + api.navigation.down() + } + + // Select + api.navigation.triggerControl("NewScoreDialog", "SelectPanel", "Select") + } + + // Done + api.navigation.triggerControl("NewScoreDialog", "BottomPanel", "Done") +} + +module.exports = { + openNewScoreDialog: openNewScoreDialog, + сhooseFluteAndPiano: сhooseFluteAndPiano, + chooseRandomInstruments: chooseRandomInstruments +}