diff --git a/src/appshell/qml/NotationPage/NotationPage.qml b/src/appshell/qml/NotationPage/NotationPage.qml index 46bf091d4295a..7a5b27a72f921 100644 --- a/src/appshell/qml/NotationPage/NotationPage.qml +++ b/src/appshell/qml/NotationPage/NotationPage.qml @@ -554,6 +554,10 @@ DockPage { Component.onDestruction: { percussionPanel.toolbarComponent = null } + + onResizeRequested: function(newWidth, newHeight) { + percussionPanel.resize(newWidth, newHeight) + } } } ] diff --git a/src/appshell/qml/Preferences/PercussionPreferencesPage.qml b/src/appshell/qml/Preferences/PercussionPreferencesPage.qml index 34a219e713d12..6bb8d5993524b 100644 --- a/src/appshell/qml/Preferences/PercussionPreferencesPage.qml +++ b/src/appshell/qml/Preferences/PercussionPreferencesPage.qml @@ -43,13 +43,14 @@ PreferencesPage { id: percussionPanelPreferences title: qsTrc("appshell/preferences", "Percussion") + rowSpacing: 20 navigation.section: root.navigationSection CheckBox { id: unpitchedSelectedCheckbox - visible: percussionPreferencesModel.useNewPercussionPanel + enabled: percussionPreferencesModel.useNewPercussionPanel width: parent.width text: qsTrc("appshell/preferences", "Open the percussion panel when an unpitched staff is selected") @@ -65,109 +66,138 @@ PreferencesPage { } } - StyledTextLabel { - id: padSwapInfo + Column { + id: swappingOptionsColumn - visible: percussionPreferencesModel.useNewPercussionPanel width: parent.width + spacing: 12 - horizontalAlignment: Text.AlignLeft - wrapMode: Text.Wrap - text: qsTrc("notation/percussion", "When swapping the positions of two drum pads:") - } - - RadioButtonGroup { - id: radioButtons + StyledTextLabel { + id: padSwapInfo - property int navigationRowStart: unpitchedSelectedCheckbox.navigation.row + 1 - property int navigationRowEnd: radioButtons.navigationRowStart + model.length + enabled: percussionPreferencesModel.useNewPercussionPanel + width: parent.width - visible: percussionPreferencesModel.useNewPercussionPanel + horizontalAlignment: Text.AlignLeft + wrapMode: Text.Wrap + text: qsTrc("notation/percussion", "When swapping the positions of two drum pads:") + } - width: parent.width - spacing: percussionPanelPreferences.spacing + RadioButtonGroup { + id: radioButtons - orientation: ListView.Vertical + property int navigationRowStart: unpitchedSelectedCheckbox.navigation.row + 1 + property int navigationRowEnd: radioButtons.navigationRowStart + model.length - model: [ - { text: qsTrc("notation/percussion", "Move MIDI notes and keyboard shortcuts with their sounds"), value: true }, - { text: qsTrc("notation/percussion", "Leave MIDI notes and keyboard shortcuts fixed to original pad positions"), value: false } - ] + enabled: percussionPreferencesModel.useNewPercussionPanel - delegate: Row { width: parent.width - spacing: 6 + spacing: swappingOptionsColumn.spacing + + orientation: ListView.Vertical + + model: [ + { text: qsTrc("notation/percussion", "Move MIDI notes and keyboard shortcuts with their sounds"), value: true }, + { text: qsTrc("notation/percussion", "Leave MIDI notes and keyboard shortcuts fixed to original pad positions"), value: false } + ] + + delegate: Row { + width: parent.width + spacing: 6 - RoundedRadioButton { - id: radioButton + RoundedRadioButton { + id: radioButton - anchors.verticalCenter: parent.verticalCenter + anchors.verticalCenter: parent.verticalCenter - navigation.name: modelData.text - navigation.panel: percussionPanelPreferences.navigation - navigation.row: radioButtons.navigationRowStart + model.index + navigation.name: modelData.text + navigation.panel: percussionPanelPreferences.navigation + navigation.row: radioButtons.navigationRowStart + model.index - checked: modelData.value === percussionPreferencesModel.percussionPanelMoveMidiNotesAndShortcuts + checked: modelData.value === percussionPreferencesModel.percussionPanelMoveMidiNotesAndShortcuts - onToggled: { - percussionPreferencesModel.percussionPanelMoveMidiNotesAndShortcuts = modelData.value + onToggled: { + percussionPreferencesModel.percussionPanelMoveMidiNotesAndShortcuts = modelData.value + } } - } - //! NOTE: Can't use radioButton.text because it won't wrap - StyledTextLabel { - width: parent.width - parent.spacing - radioButton.width + //! NOTE: Can't use radioButton.text because it won't wrap + StyledTextLabel { + width: parent.width - parent.spacing - radioButton.width - anchors.verticalCenter: parent.verticalCenter + anchors.verticalCenter: parent.verticalCenter - horizontalAlignment: Text.AlignLeft - wrapMode: Text.Wrap - text: modelData.text + horizontalAlignment: Text.AlignLeft + wrapMode: Text.Wrap + text: modelData.text - MouseArea { - id: mouseArea + MouseArea { + id: mouseArea - anchors.fill: parent + anchors.fill: parent - onClicked: { - percussionPreferencesModel.percussionPanelMoveMidiNotesAndShortcuts = modelData.value + onClicked: { + percussionPreferencesModel.percussionPanelMoveMidiNotesAndShortcuts = modelData.value + } } } } } + + CheckBox { + id: alwaysAsk + + enabled: percussionPreferencesModel.useNewPercussionPanel + width: parent.width + + text: qsTrc("global", "Always ask") + + navigation.name: "AlwaysAskCheckBox" + navigation.panel: percussionPanelPreferences.navigation + navigation.row: radioButtons.navigationRowEnd + + checked: percussionPreferencesModel.showPercussionPanelPadSwapDialog + + onClicked: { + percussionPreferencesModel.showPercussionPanelPadSwapDialog = !alwaysAsk.checked + } + } } - CheckBox { - id: alwaysAsk + Row { + id: useLegacyToggleRow - visible: percussionPreferencesModel.useNewPercussionPanel + height: useLegacyToggle.height width: parent.width - text: qsTrc("global", "Always ask") + spacing: 6 - navigation.name: "AlwaysAskCheckBox" - navigation.panel: percussionPanelPreferences.navigation - navigation.row: radioButtons.navigationRowEnd + ToggleButton { + id: useLegacyToggle - checked: percussionPreferencesModel.showPercussionPanelPadSwapDialog + checked: !percussionPreferencesModel.useNewPercussionPanel - onClicked: { - percussionPreferencesModel.showPercussionPanelPadSwapDialog = !alwaysAsk.checked + navigation.name: "UseLegacyPercussionPanel" + navigation.panel: percussionPanelPreferences.navigation + navigation.row: alwaysAsk.navigation.row + 1 + + onToggled: { + percussionPreferencesModel.useNewPercussionPanel = !percussionPreferencesModel.useNewPercussionPanel + } } - } - FlatButton { - id: useNewPercussionPanel + StyledTextLabel { + id: legacyToggleInfo - text: percussionPreferencesModel.useNewPercussionPanel ? qsTrc("notation/percussion", "Switch to old percussion panel") - : qsTrc("notation/percussion", "Switch to new percussion panel") + enabled: percussionPreferencesModel.useNewPercussionPanel - navigation.name: "SwitchPercussionPanels" - navigation.panel: percussionPanelPreferences.navigation - navigation.row: alwaysAsk.navigation.row + 1 + height: parent.height + + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter - onClicked: { - percussionPreferencesModel.useNewPercussionPanel = !percussionPreferencesModel.useNewPercussionPanel + wrapMode: Text.Wrap + text: qsTrc("notation/percussion", "Use legacy percussion panel") } } } diff --git a/src/engraving/dom/noteentry.cpp b/src/engraving/dom/noteentry.cpp index ead54c0198d31..5edbafdb52d20 100644 --- a/src/engraving/dom/noteentry.cpp +++ b/src/engraving/dom/noteentry.cpp @@ -211,8 +211,9 @@ Note* Score::addPitch(NoteVal& nval, bool addFlag, InputState* externalInputStat } if (!is.cr()) { - return 0; + handleOverlappingChordRest(is); } + Measure* measure = is.segment()->measure(); if (measure->isMeasureRepeatGroup(track2staff(track))) { MeasureRepeat* mr = measure->measureRepeatElement(track2staff(track)); @@ -456,29 +457,7 @@ Ret Score::putNote(const Position& p, bool replace) // If there's an overlapping ChordRest at the current input position, shorten it... if (!cr) { - MasterScore* ms = masterScore(); - ChordRest* prevCr = m_is.segment()->nextChordRest(m_is.track(), /*backwards*/ true, /*stopAtMeasureBoundary*/ true); - if (prevCr && prevCr->endTick() > m_is.tick()) { - const Fraction overlapDuration = prevCr->endTick() - m_is.tick(); - const Fraction desiredDuration = prevCr->ticks() - overlapDuration; - - const InputState inputStateToRestore = m_is; // because changeCRlen will alter the input state - ms->changeCRlen(prevCr, desiredDuration, /*fillWithRest*/ false); - - // Fill the difference with tied notes if necessary... - const Fraction difference = desiredDuration - prevCr->ticks(); - if (prevCr->isChord() && difference.isNotZero()) { - Fraction startTick = prevCr->endTick(); - Chord* prevChord = toChord(prevCr); - const std::vector durationList = toDurationList(difference, true); - for (const TDuration& dur : durationList) { - prevChord = ms->addChord(startTick, dur, prevChord, /*genTie*/ bool(prevChord), prevChord->tuplet()); - startTick += dur.fraction(); - } - } - - m_is = inputStateToRestore; - } + handleOverlappingChordRest(m_is); } auto checkTied = [&](){ @@ -587,6 +566,33 @@ Ret Score::putNote(const Position& p, bool replace) return ret; } +void Score::handleOverlappingChordRest(InputState& inputState) +{ + MasterScore* ms = masterScore(); + ChordRest* prevCr = inputState.segment()->nextChordRest(inputState.track(), /*backwards*/ true, /*stopAtMeasureBoundary*/ true); + if (prevCr && prevCr->endTick() > inputState.tick()) { + const Fraction overlapDuration = prevCr->endTick() - inputState.tick(); + const Fraction desiredDuration = prevCr->ticks() - overlapDuration; + + const InputState inputStateToRestore = inputState; // because changeCRlen will alter the input state + ms->changeCRlen(prevCr, desiredDuration, /*fillWithRest*/ true); + + // Fill the difference with tied notes if necessary... + const Fraction difference = desiredDuration - prevCr->ticks(); + if (prevCr->isChord() && difference.isNotZero()) { + Fraction startTick = prevCr->endTick(); + Chord* prevChord = toChord(prevCr); + const std::vector durationList = toDurationList(difference, true); + for (const TDuration& dur : durationList) { + prevChord = ms->addChord(startTick, dur, prevChord, /*genTie*/ bool(prevChord), prevChord->tuplet()); + startTick += dur.fraction(); + } + } + + inputState = inputStateToRestore; + } +} + //--------------------------------------------------------- // repitchNote //--------------------------------------------------------- diff --git a/src/engraving/dom/score.h b/src/engraving/dom/score.h index 7e1b80e648e33..a996e3e3cd1c7 100644 --- a/src/engraving/dom/score.h +++ b/src/engraving/dom/score.h @@ -1072,6 +1072,7 @@ class Score : public EngravingObject, public muse::Injectable void selectRange(EngravingItem* e, staff_idx_t staffIdx); muse::Ret putNote(const Position&, bool replace); + void handleOverlappingChordRest(InputState& inputState); void resetTempo(); void resetTempoRange(const Fraction& tick1, const Fraction& tick2); diff --git a/src/engraving/dom/select.cpp b/src/engraving/dom/select.cpp index 9e8c37cc31526..95a3431737cdc 100644 --- a/src/engraving/dom/select.cpp +++ b/src/engraving/dom/select.cpp @@ -817,10 +817,11 @@ void Selection::setRange(Segment* startSegment, Segment* endSegment, staff_idx_t assert(!(endSegment && !startSegment)); m_startSegment = startSegment; - m_endSegment = endSegment; + m_endSegment = endSegment; m_activeSegment = endSegment; - m_staffStart = staffStart; - m_staffEnd = staffEnd; + m_staffStart = staffStart; + m_staffEnd = staffEnd; + m_activeTrack = staff2track(staffStart); if (m_state == SelState::RANGE) { m_score->setSelectionChanged(true); @@ -844,8 +845,9 @@ void Selection::setRangeTicks(const Fraction& tick1, const Fraction& tick2, staf m_plannedTick1 = tick1; m_plannedTick2 = tick2; m_startSegment = m_endSegment = m_activeSegment = nullptr; - m_staffStart = staffStart; - m_staffEnd = staffEnd; + m_staffStart = staffStart; + m_staffEnd = staffEnd; + m_activeTrack = staff2track(staffStart); if (m_state == SelState::RANGE) { m_score->setSelectionChanged(true); diff --git a/src/notation/internal/notationinteraction.cpp b/src/notation/internal/notationinteraction.cpp index 8554c159a8dde..b6fc001eb4e34 100644 --- a/src/notation/internal/notationinteraction.cpp +++ b/src/notation/internal/notationinteraction.cpp @@ -196,11 +196,11 @@ NotationInteraction::NotationInteraction(Notation* notation, INotationUndoStackP m_undoStack->undoRedoNotification().onNotify(this, [this]() { endEditElement(); - notifyAboutNoteInputStateChanged(); }); m_undoStack->stackChanged().onNotify(this, [this]() { notifyAboutSelectionChangedIfNeed(); + notifyAboutNoteInputStateChanged(); }); m_dragData.ed = mu::engraving::EditData(&m_scoreCallbacks); diff --git a/src/notation/qml/MuseScore/NotationScene/PercussionPanel.qml b/src/notation/qml/MuseScore/NotationScene/PercussionPanel.qml index d97bc554e5afe..d01dbf4e2db24 100644 --- a/src/notation/qml/MuseScore/NotationScene/PercussionPanel.qml +++ b/src/notation/qml/MuseScore/NotationScene/PercussionPanel.qml @@ -34,6 +34,8 @@ Item { property NavigationSection navigationSection: null property int contentNavigationPanelOrderStart: 1 + signal resizeRequested(var newWidth, var newHeight) + anchors.fill: parent property Component toolbarComponent: PercussionPanelToolBar { @@ -47,6 +49,8 @@ Item { Component.onCompleted: { padGrid.model.init() + var newHeight = (padGrid.numRows * padGrid.cellHeight) + (soundTitleLabel.height * 2) + root.resizeRequested(root.width, newHeight) } PercussionPanelModel { @@ -325,6 +329,11 @@ Item { } pad.padNavigation.requestActive() } + + function onNumPadsChanged() { + var newHeight = (padGrid.numRows * padGrid.cellHeight) + (soundTitleLabel.height * 2) + root.resizeRequested(root.width, newHeight) + } } } diff --git a/src/notation/view/percussionpanel/percussionpanelpadlistmodel.cpp b/src/notation/view/percussionpanel/percussionpanelpadlistmodel.cpp index c8f4c23f2dc57..c2ba624a057e1 100644 --- a/src/notation/view/percussionpanel/percussionpanelpadlistmodel.cpp +++ b/src/notation/view/percussionpanel/percussionpanelpadlistmodel.cpp @@ -192,8 +192,9 @@ mu::engraving::Drumset PercussionPanelPadListModel::constructDefaultLayout(const QList noTemplateFound; for (int pitch = 0; pitch < mu::engraving::DRUM_INSTRUMENTS; ++pitch) { - if (!defaultLayout.isValid(pitch)) { - // We aren't currently using this pitch, doesn't matter if it's valid in the template.. + if (defaultDrumset->isValid(pitch) && !defaultLayout.isValid(pitch)) { + // Pitch was deleted - restore it... + defaultLayout.drum(pitch) = defaultDrumset->drum(pitch); continue; } //! NOTE: Pitch + drum name isn't exactly the most robust identifier, but this will probably change with the new percussion ID system @@ -351,7 +352,7 @@ PercussionPanelPadModel* PercussionPanelPadListModel::createPadModelForPitch(int PercussionPanelPadModel* model = new PercussionPanelPadModel(this); model->setPadName(m_drumset->name(pitch)); - const QString shortcut = m_drumset->shortcut(pitch) ? QChar(m_drumset->shortcut(pitch)) : QString("-"); + const QString shortcut = m_drumset->shortcut(pitch) ? QChar(m_drumset->shortcut(pitch)) : QChar('\0'); model->setKeyboardShortcut(shortcut); model->setPitch(pitch); diff --git a/src/notation/view/percussionpanel/percussionpanelpadmodel.cpp b/src/notation/view/percussionpanel/percussionpanelpadmodel.cpp index d9f2b94a90d4a..4f7c5240524d2 100644 --- a/src/notation/view/percussionpanel/percussionpanelpadmodel.cpp +++ b/src/notation/view/percussionpanel/percussionpanelpadmodel.cpp @@ -83,13 +83,14 @@ const QVariant PercussionPanelPadModel::notationPreviewItemVariant() const QList PercussionPanelPadModel::footerContextMenuItems() const { - static constexpr int duplicatePadIcon = static_cast(IconCode::Code::COPY); + // static constexpr int duplicatePadIcon = static_cast(IconCode::Code::COPY); static constexpr int deletePadIcon = static_cast(IconCode::Code::DELETE_TANK); static constexpr int definePadShortcutIcon = static_cast(IconCode::Code::SHORTCUTS); QList menuItems = { - { { "id", DUPLICATE_PAD_CODE }, { "title", muse::qtrc("global", "Duplicate") }, - { "icon", duplicatePadIcon }, { "enabled", true } }, + //! NOTE: Disabled for now - will be re-introduced with new percussion mapping system... + // { { "id", DUPLICATE_PAD_CODE }, { "title", muse::qtrc("global", "Duplicate") }, + // { "icon", duplicatePadIcon }, { "enabled", true } }, { { "id", DELETE_PAD_CODE }, { "title", muse::qtrc("global", "Delete") }, { "icon", deletePadIcon }, { "enabled", true } },