From 93558c3ca4955c60b18dbc96bb20ac80af83adc3 Mon Sep 17 00:00:00 2001 From: Roman Pudashkin Date: Mon, 10 Feb 2025 13:22:28 +0200 Subject: [PATCH] fix #26364: draw accidentals in input by duration --- src/engraving/dom/utils.cpp | 44 ++++++++-- src/engraving/dom/utils.h | 2 + src/notation/internal/notationinteraction.cpp | 86 +++++++++++-------- src/notation/internal/notationinteraction.h | 7 +- src/notation/internal/notationnoteinput.cpp | 7 ++ 5 files changed, 102 insertions(+), 44 deletions(-) diff --git a/src/engraving/dom/utils.cpp b/src/engraving/dom/utils.cpp index c1f85d7ca8a39..0e8eb3e0683d3 100644 --- a/src/engraving/dom/utils.cpp +++ b/src/engraving/dom/utils.cpp @@ -27,6 +27,7 @@ #include "containers.h" +#include "accidental.h" #include "chord.h" #include "chordrest.h" #include "clef.h" @@ -1133,6 +1134,23 @@ int chromaticPitchSteps(const Note* noteL, const Note* noteR, const int nominalD return halfsteps; } +static void noteValToEffectivePitchAndTpc(const NoteVal& nval, const Staff* staff, const Fraction& tick, int& epitch, int& tpc) +{ + const bool concertPitch = staff->concertPitch(); + + if (concertPitch) { + epitch = nval.pitch; + } else { + const int pitchOffset = staff->part()->instrument(tick)->transpose().chromatic; + epitch = nval.pitch - pitchOffset; + } + + tpc = nval.tpc(concertPitch); + if (tpc == static_cast(mu::engraving::Tpc::TPC_INVALID)) { + tpc = pitch2tpc(epitch, staff->key(tick), mu::engraving::Prefer::NEAREST); + } +} + int noteValToLine(const NoteVal& nval, const Staff* staff, const Fraction& tick) { if (staff->isDrumStaff(tick)) { @@ -1146,16 +1164,28 @@ int noteValToLine(const NoteVal& nval, const Staff* staff, const Fraction& tick) return staff->middleLine(tick); } - const bool concertPitch = staff->concertPitch(); - const int pitchOffset = concertPitch ? 0 : staff->part()->instrument(tick)->transpose().chromatic; - const int epitch = nval.pitch - pitchOffset; + int epitch = nval.pitch; + int tpc = static_cast(mu::engraving::Tpc::TPC_INVALID); + noteValToEffectivePitchAndTpc(nval, staff, tick, epitch, tpc); - int tpc = nval.tpc(concertPitch); - if (tpc == static_cast(mu::engraving::Tpc::TPC_INVALID)) { - tpc = pitch2tpc(epitch, staff->key(tick), mu::engraving::Prefer::NEAREST); + return relStep(epitch, tpc, staff->clef(tick)); +} + +AccidentalType noteValToAccidentalType(const NoteVal& nval, const Staff* staff, const Fraction& tick) +{ + if (nval.isRest()) { + return AccidentalType::NONE; } - return relStep(epitch, tpc, staff->clef(tick)); + if (staff->isDrumStaff(tick)) { + return AccidentalType::NONE; + } + + int epitch = nval.pitch; + int tpc = static_cast(mu::engraving::Tpc::TPC_INVALID); + noteValToEffectivePitchAndTpc(nval, staff, tick, epitch, tpc); + + return Accidental::value2subtype(tpc2alter(tpc)); } int compareNotesPos(const Note* n1, const Note* n2) diff --git a/src/engraving/dom/utils.h b/src/engraving/dom/utils.h index d7a66ebb4e330..4ba9b87603fd2 100644 --- a/src/engraving/dom/utils.h +++ b/src/engraving/dom/utils.h @@ -23,6 +23,7 @@ #pragma once #include "../types/types.h" +#include "types.h" #include "interval.h" @@ -85,6 +86,7 @@ extern int pitch2step(int pitch); extern int step2pitch(int step); int chromaticPitchSteps(const Note* noteL, const Note* noteR, const int nominalDiatonicSteps); extern int noteValToLine(const NoteVal& nval, const Staff* staff, const Fraction& tick); +extern AccidentalType noteValToAccidentalType(const NoteVal& nval, const Staff* staff, const Fraction& tick); extern int compareNotesPos(const Note* n1, const Note* n2); extern Segment* skipTuplet(Tuplet* tuplet); diff --git a/src/notation/internal/notationinteraction.cpp b/src/notation/internal/notationinteraction.cpp index 7b7a5eff90ede..cc903ade87319 100644 --- a/src/notation/internal/notationinteraction.cpp +++ b/src/notation/internal/notationinteraction.cpp @@ -44,6 +44,7 @@ #include "draw/types/pen.h" #include "engraving/internal/qmimedataadapter.h" +#include "engraving/dom/accidental.h" #include "engraving/dom/actionicon.h" #include "engraving/dom/anchors.h" #include "engraving/dom/articulation.h" @@ -367,23 +368,24 @@ bool NotationInteraction::showShadowNote(const PointF& pos) const mu::engraving::InputState& inputState = score()->inputState(); mu::engraving::ShadowNote& shadowNote = *score()->shadowNote(); - mu::engraving::Position position; - if (!score()->getPosition(&position, pos, inputState.voice())) { + InputNoteParams params; + if (!score()->getPosition(¶ms.position, pos, inputState.voice())) { shadowNote.setVisible(false); return false; } - ShadowNoteParams params; params.duration = inputState.duration(); params.accidentalType = inputState.accidentalType(); params.articulationIds = inputState.articulationIds(); - showShadowNoteAtPosition(shadowNote, params, position); + showShadowNote(shadowNote, params); return true; } -void NotationInteraction::showShadowNoteAtPosition(ShadowNote& shadowNote, const ShadowNoteParams& params, Position& position) +void NotationInteraction::showShadowNote(ShadowNote& shadowNote, InputNoteParams& params) { + Position& position = params.position; + const mu::engraving::InputState& inputState = score()->inputState(); const Staff* staff = score()->staff(position.staffIdx); const mu::engraving::Instrument* instr = staff->part()->instrument(); @@ -2678,20 +2680,21 @@ double NotationInteraction::currentScaling(Painter* painter) const return painter->worldTransform().m11() / guiScaling; } -std::vector NotationInteraction::inputPositions() const +std::vector NotationInteraction::inputNotes() const { - std::vector result; + std::vector result; const InputState& is = score()->inputState(); if (!is.isValid()) { return result; } + Segment* segment = is.segment(); const staff_idx_t staffIdx = is.staffIdx(); const Staff* staff = score()->staff(staffIdx); - const System* system = is.segment()->system(); + const System* system = segment->system(); const SysStaff* sysStaff = system ? system->staff(staffIdx) : nullptr; - const Measure* measure = is.segment()->measure(); + const Measure* measure = segment->measure(); if (!staff || !sysStaff || !measure) { return result; @@ -2711,20 +2714,38 @@ std::vector NotationInteraction::inputPositions() const nvals = is.notes(); } - Position pos; - pos.segment = is.segment(); - pos.staffIdx = staffIdx; + InputNoteParams params; + params.duration = is.rest() ? is.duration() : TDuration(); + params.position.segment = segment; + params.position.staffIdx = staffIdx; + + if (!is.rest() && is.accidentalType() != AccidentalType::NONE) { + params.accidentalType = is.accidentalType(); + } for (const NoteVal& nval : nvals) { - pos.line = mu::engraving::noteValToLine(nval, staff, tick); - const double y = sysStaff->y() + pos.line * lineDist; - pos.pos = PointF(is.segment()->x(), y) + measurePos; + const int line = mu::engraving::noteValToLine(nval, staff, tick); + const double y = sysStaff->y() + line * lineDist; + + if (params.accidentalType == AccidentalType::NONE) { + const AccidentalType type = mu::engraving::noteValToAccidentalType(nval, staff, tick); + if (type != AccidentalType::NATURAL) { + bool error = false; + const AccidentalVal existingAccVal = measure->findAccidental(segment, staffIdx, line, error); + if (!error && type != Accidental::value2subtype(existingAccVal)) { + params.accidentalType = type; + } + } + } + + params.position.line = line; + params.position.pos = PointF(segment->x(), y) + measurePos; - result.push_back(pos); + result.push_back(params); } - std::sort(result.begin(), result.end(), [](const Position& p1, const Position& p2) { - return p1.line < p2.line; + std::sort(result.begin(), result.end(), [](const InputNoteParams& p1, const InputNoteParams& p2) { + return p1.position.line < p2.position.line; }); return result; @@ -2737,31 +2758,28 @@ bool NotationInteraction::shouldDrawInputPreview() const void NotationInteraction::drawInputPreview(Painter* painter) { - std::vector positions = inputPositions(); - if (positions.empty()) { + std::vector inputNotes = this->inputNotes(); + if (inputNotes.empty()) { return; } - std::vector notes; - notes.reserve(positions.size()); + std::vector previewList; + previewList.reserve(inputNotes.size()); const InputState& is = score()->inputState(); - ShadowNoteParams params; - params.duration = is.rest() ? is.duration() : TDuration(); - - for (Position& pos : positions) { - ShadowNote* note = new ShadowNote(score()); - showShadowNoteAtPosition(*note, params, pos); - notes.push_back(note); + for (InputNoteParams& params : inputNotes) { + ShadowNote* preview = new ShadowNote(score()); + showShadowNote(*preview, params); + previewList.push_back(preview); } - const bool isUp = !is.rest() && notes.front()->computeUp(); + const bool isUp = !is.rest() && previewList.front()->computeUp(); bool isLeft = isUp; int prevLine = INT_MAX; auto correctNotePositionIfNeed = [&](ShadowNote* note) { - if (positions.size() == 1) { + if (inputNotes.size() == 1) { return; } @@ -2786,18 +2804,18 @@ void NotationInteraction::drawInputPreview(Painter* painter) }; if (isUp) { - for (auto it = notes.rbegin(); it != notes.rend(); ++it) { + for (auto it = previewList.rbegin(); it != previewList.rend(); ++it) { correctNotePositionIfNeed(*it); score()->renderer()->drawItem(*it, painter); } } else { - for (auto it = notes.begin(); it != notes.end(); ++it) { + for (auto it = previewList.begin(); it != previewList.end(); ++it) { correctNotePositionIfNeed(*it); score()->renderer()->drawItem(*it, painter); } } - DeleteAll(notes); + DeleteAll(previewList); } void NotationInteraction::drawAnchorLines(Painter* painter) diff --git a/src/notation/internal/notationinteraction.h b/src/notation/internal/notationinteraction.h index 5435c0cf8fa8a..9c3deb00a12e5 100644 --- a/src/notation/internal/notationinteraction.h +++ b/src/notation/internal/notationinteraction.h @@ -317,13 +317,14 @@ class NotationInteraction : public INotationInteraction, public muse::Injectable void apply(); void rollback(); - struct ShadowNoteParams { + struct InputNoteParams { mu::engraving::TDuration duration; mu::engraving::AccidentalType accidentalType = mu::engraving::AccidentalType::NONE; std::set articulationIds; + mu::engraving::Position position; }; - void showShadowNoteAtPosition(mu::engraving::ShadowNote& note, const ShadowNoteParams& params, mu::engraving::Position& pos); + void showShadowNote(mu::engraving::ShadowNote& note, InputNoteParams& params); bool needStartEditGrip(QKeyEvent* event) const; bool handleKeyPress(QKeyEvent* event); @@ -379,7 +380,7 @@ class NotationInteraction : public INotationInteraction, public muse::Injectable void resetAnchorLines(); double currentScaling(muse::draw::Painter* painter) const; - std::vector inputPositions() const; + std::vector inputNotes() const; bool shouldDrawInputPreview() const; void drawInputPreview(muse::draw::Painter* painter); diff --git a/src/notation/internal/notationnoteinput.cpp b/src/notation/internal/notationnoteinput.cpp index 8f3250ad4ff18..2596552d55956 100644 --- a/src/notation/internal/notationnoteinput.cpp +++ b/src/notation/internal/notationnoteinput.cpp @@ -458,6 +458,13 @@ void NotationNoteInput::padNote(const Pad& pad) score()->padToggle(pad); apply(); + if (pad >= Pad::NOTE00 && pad <= Pad::NOTE1024) { + const NoteInputState& is = score()->inputState(); + if (!is.rest() && is.usingNoteEntryMethod(NoteInputMethod::BY_DURATION)) { + score()->toggleAccidental(AccidentalType::NONE); + } + } + notifyAboutStateChanged(); MScoreErrorsController(iocContext()).checkAndShowMScoreError();