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

[CMD: up/down chord] Traverse as list after range selection #24400

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
62 changes: 62 additions & 0 deletions src/engraving/dom/chordrest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
#include "instrchange.h"
#include "keysig.h"
#include "lyrics.h"
#include "mcursor.h"
#include "measure.h"
#include "navigate.h"
#include "note.h"
Expand Down Expand Up @@ -1291,4 +1292,65 @@ void ChordRest::checkStaffMoveValidity()
m_storedStaffMove = 0;
}
}

//----------------------------------------------------------------
// getNotesAtPosition - Get all notes corresponding
// to the vertical segment of a ChordRest input. Only one
// instrument - the ChordRest's instrument - is taken into
// account if onlyOne = true, else the entire score -
// currently not implemented for pianoview
//----------------------------------------------------------------

void ChordRest::getNotesAtPosition(std::vector<Note*>& notesAtPosition, bool onlyOne)
{
auto part = staff()->part();
auto firstTrackOfPart = onlyOne ? part->startTrack() : 0;
auto lastTrackOfPart = onlyOne ? part->endTrack() : score()->ntracks();
auto currentPosition = tick();

MCursor c;
c.setScore(score()->masterScore());
if (!notesAtPosition.empty()) {
notesAtPosition.clear();
}
for (track_idx_t track = firstTrackOfPart; track < lastTrackOfPart; track++) {
c.move(static_cast<int>(track), currentPosition);
if (auto e = c.currentElement()) {
if (e->isChord()) {
auto notes = toChord(e)->notes();
for (auto note : notes) {
notesAtPosition.emplace_back(note);
}
}
}
}
}

//----------------------------------------------------------------
// getChordRestsAtPosition - same as getNotesAtPosition except
// now stores ChordRests to include rests for the purposes
// of vertical movement of selection of the same tick
//----------------------------------------------------------------

void ChordRest::getChordRestsAtPosition(std::vector<ChordRest*>& chordRestsAtPosition, bool onlyOne)
{
auto part = staff()->part();
auto firstTrackOfPart = onlyOne ? part->startTrack() : 0;
auto lastTrackOfPart = onlyOne ? part->endTrack() : score()->ntracks();
auto currentPosition = tick();

MCursor c;
c.setScore(score()->masterScore());
if (!chordRestsAtPosition.empty()) {
chordRestsAtPosition.clear();
}
for (track_idx_t track = firstTrackOfPart; track < lastTrackOfPart; track++) {
c.move(static_cast<int>(track), currentPosition);
if (auto e = c.currentElement()) {
if (e->isChord() || e->isRest()) {
chordRestsAtPosition.emplace_back(toChordRest(e));
}
}
}
}
}
3 changes: 3 additions & 0 deletions src/engraving/dom/chordrest.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,9 @@ class ChordRest : public DurationElement
void removeDeleteBeam(bool beamed);
void replaceBeam(Beam* newBeam);

void getNotesAtPosition(std::vector<Note*>&, bool onlyOne = true);
void getChordRestsAtPosition(std::vector<ChordRest*>& chordRestsAtPosition, bool onlyOne = true);

const ElementList& el() const { return m_el; }

//! TODO Look like a hack, see using
Expand Down
11 changes: 7 additions & 4 deletions src/engraving/dom/cmd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2860,8 +2860,8 @@ EngravingItem* Score::move(const String& cmd)
// to catch up with the cursor and not move the selection by 2 positions
cr = selection().cr();
if (cr && (cr->isGrace() || cmd == u"next-chord" || cmd == u"prev-chord")) {
} else {
cr = inputState().cr();
} else if (auto is = inputState().cr()) {
cr = is;
}
} else if (selection().activeCR()) {
cr = selection().activeCR();
Expand Down Expand Up @@ -3012,8 +3012,11 @@ EngravingItem* Score::move(const String& cmd)
// selection "cursor"
// find previous chordrest, which might be a grace note
// this may override note input cursor
el = prevChordRest(cr);

if (auto pcr = prevChordRest(cr)) {
if (prevChordRest(cr)->isGrace()) {
el = pcr;
}
}
// Skip gap rests if we're not in note entry mode...
while (!noteEntryMode() && el && el->isRest() && toRest(el)->isGap()) {
el = prevChordRest(toChordRest(el));
Expand Down
13 changes: 13 additions & 0 deletions src/engraving/dom/mcursor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,4 +182,17 @@ void MCursor::addPart(const String& instrument)
m_score->appendPart(part);
m_score->insertStaff(staff, 0);
}

//---------------------------------------------------------
// currentElement
// returns the element @ cursor position if
// a valid track & tick were set
//---------------------------------------------------------

EngravingItem* MCursor::currentElement() const
{
auto measure = m_score->tick2measure(m_tick);
auto seg = measure->getSegment(SegmentType::ChordRest, m_tick);
return seg && seg->element(m_track) ? seg->element(m_track) : nullptr;
}
}
3 changes: 3 additions & 0 deletions src/engraving/dom/mcursor.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#ifndef MU_ENGRAVING_MCURSOR_H
#define MU_ENGRAVING_MCURSOR_H

#include "engravingitem.h"
#include "types/string.h"
#include "../types/fraction.h"

Expand Down Expand Up @@ -55,6 +56,8 @@ class MCursor
void setScore(MasterScore* s) { m_score = s; }
void setTimeSig(Fraction f) { m_sig = f; }

EngravingItem* currentElement() const;

private:

void createMeasures();
Expand Down
82 changes: 82 additions & 0 deletions src/engraving/dom/navigate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include "staff.h"
#include "soundflag.h"
#include "guitarbend.h"
#include "notation/notationtypes.h"

using namespace mu;

Expand Down Expand Up @@ -380,6 +381,87 @@ Note* Score::downAltCtrl(Note* note) const
return note->chord()->downNote();
}

//---------------------------------------------------------
// moveAlt - Updated upAlt/downAlt to let tick (beat) take
// precedence - facilitate up/down traveling in a
// vertical time domain
//
// element: Note() or Rest()
// return: Note() or Rest()
//
// return next higher/lower pitched note in chord
// or top/bottom of next/previous track's chord if at wit's end
//---------------------------------------------------------

EngravingItem* Score::moveAlt(EngravingItem* element, notation::MoveDirection direction)
{
EngravingItem* result = nullptr;
ChordRest* cr = nullptr;
auto originalTrack = element->track();
bool moveUp = (direction == notation::MoveDirection::Up);
bool isNote = element->isNote();
bool isRest = element->isRest();

if (isNote) {
cr = toChordRest(element->parent());
auto note = toNote(element);
auto chord = note->chord();
const std::vector<Note*>& notes = chord->notes();
auto it = std::find(notes.begin(), notes.end(), note);
// Traverse notes within same ChordRest until at extremum
auto condition = moveUp ? notes.end() : notes.begin();
if (moveUp) {
++it;
}
if (it != condition) {
if (!moveUp) {
--it;
}
result = *it;
}
} else if (isRest) {
cr = toChordRest(element);
}

if (!result) {
// Traverse same-beat tracks
std::vector<ChordRest*> chordRestsOfBeat;
if (!cr) {
return nullptr;
}

cr->getChordRestsAtPosition(chordRestsOfBeat, false);
if (moveUp) {
std::reverse(chordRestsOfBeat.begin(), chordRestsOfBeat.end());
}

for (auto it : chordRestsOfBeat) {
auto targetCR = it;
auto targetTrack = targetCR->track();
if (moveUp && (targetTrack >= originalTrack)) {
continue;
} else if (!moveUp && (targetTrack <= originalTrack)) {
continue;
}
if (targetCR) {
result = targetCR;
}
break;
}

if (result && (result->track() == originalTrack)) {
result = element;
}
}

if (result && result->isChord()) {
auto chord = toChord(result);
result = moveUp ? chord->downNote() : chord->upNote();
}

return result;
}

//---------------------------------------------------------
// firstElement
//---------------------------------------------------------
Expand Down
5 changes: 5 additions & 0 deletions src/engraving/dom/score.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ namespace mu::engraving::compat {
class WriteScoreHook;
}

namespace mu::notation {
enum class MoveDirection;
}

namespace mu::engraving {
class Articulation;
class Audio;
Expand Down Expand Up @@ -934,6 +938,7 @@ class Score : public EngravingObject, public muse::Injectable
Note* upAltCtrl(Note*) const;
EngravingItem* downAlt(EngravingItem*);
Note* downAltCtrl(Note*) const;
EngravingItem* moveAlt(EngravingItem*, notation::MoveDirection);

EngravingItem* firstElement(bool frame = true);
EngravingItem* lastElement(bool frame = true);
Expand Down
4 changes: 2 additions & 2 deletions src/instrumentsscene/internal/selectinstrumentscenario.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ RetVal<Instrument> SelectInstrumentsScenario::selectInstrument(const InstrumentK
return selectedInstruments.ret;
}

const InstrumentTemplate& templ = selectedInstruments.val.instruments.first().instrumentTemplate;
const InstrumentTemplate& templ = selectedInstruments.val.instruments.front().instrumentTemplate;

return RetVal<Instrument>::make_ok(Instrument::fromTemplate(&templ));
}
Expand Down Expand Up @@ -82,7 +82,7 @@ RetVal<PartInstrumentListScoreOrder> SelectInstrumentsScenario::selectInstrument
String instrumentId = String::fromStdString(map["instrumentId"].toString());
pi.instrumentTemplate = instrumentsRepository()->instrumentTemplate(instrumentId);

result.instruments << pi;
result.instruments.push_back(pi);
}

ValMap order = content["scoreOrder"].toMap();
Expand Down
2 changes: 1 addition & 1 deletion src/instrumentsscene/view/staffsettingsmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ QVariantList StaffSettingsModel::allStaffTypes() const
if (isTypeAllowed(type)) {
QVariantMap obj;

obj["text"] = staffTypeToString(type.type());
obj["text"] = staffTypeToString(type.type()).toQString();
obj["value"] = static_cast<int>(type.type());

result << obj;
Expand Down
2 changes: 1 addition & 1 deletion src/notation/internal/masternotationparts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ void MasterNotationParts::setParts(const PartInstrumentList& partList, const Sco
PartInstrument pi;
pi.isExistingPart = true;
pi.partId = part->id();
excerptPartList << pi;
excerptPartList.push_back(pi);
}

impl->sortParts(excerptPartList);
Expand Down
77 changes: 65 additions & 12 deletions src/notation/internal/notationinteraction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -689,24 +689,77 @@ void NotationInteraction::moveChordNoteSelection(MoveDirection d)
return;
}

EngravingItem* current = selection()->element();
if (!current || !(current->isNote() || current->isRest())) {
auto removeDuplicates = [](std::vector<EngravingItem*>& elements) {
std::sort(elements.begin(), elements.end());
auto it = std::unique(elements.begin(), elements.end());
elements.erase(it, elements.end());
};

auto& _score = *score();
auto& selection = _score.selection();
EngravingItem* currentSingle = selection.element();
EngravingItem* oldSingle = currentSingle;
bool isRange = selection.isRange();
std::vector<EngravingItem*> notes;

// Single traverse:
if (currentSingle && (currentSingle->isNote() || currentSingle->isRest())) {
EngravingItem* newSingle = _score.moveAlt(currentSingle, d);
if (newSingle == currentSingle) {
return;
}
while (newSingle && newSingle->isRest() && toRest(newSingle)->isGap()) {
newSingle = _score.moveAlt(newSingle, d);
if (newSingle == oldSingle) {
break;
}
}
if (newSingle) {
select({ newSingle }, SelectType::SINGLE, newSingle->staffIdx());
showItem(newSingle);
}
return;
}

EngravingItem* chordElem;
if (d == MoveDirection::Up) {
chordElem = score()->upAlt(current);
} else {
chordElem = score()->downAlt(current);
// Range/List traverse:
else {
for (auto item : selection.elements()) {
if (item->isNote()) {
auto selectedNote = toNote(item);
if (isRange) {
auto newSelection = (d == MoveDirection::Down)
? selectedNote->chord()->downNote()
: selectedNote->chord()->upNote();
notes.emplace_back(newSelection);
} else { // List
auto newSelection = _score.moveAlt(selectedNote, d);
bool keepSelection = !newSelection;
if (newSelection) {
if (newSelection->isNote()) {
auto newNote = toNote(newSelection);
bool sameChord = (newNote->chord() == selectedNote->chord());
if (!sameChord) {
keepSelection = true;
}
} else if (newSelection->isRest()) {
keepSelection = true;
}
}
notes.emplace_back(keepSelection ? selectedNote : newSelection);
}
}
removeDuplicates(notes);
}
}

if (chordElem == current) {
if (!notes.empty()) {
selection.clear();
for (auto& note : notes) {
select({ note }, SelectType::ADD, note->staffIdx());
showItem(note);
}
// score.update();
return;
}

select({ chordElem }, SelectType::SINGLE, chordElem->staffIdx());
showItem(chordElem);
}

void NotationInteraction::moveSegmentSelection(MoveDirection d)
Expand Down
Loading