From 00990679310d5782f199c633c8e385b0bb698aff Mon Sep 17 00:00:00 2001 From: Michele Spagnolo Date: Mon, 19 May 2025 14:41:14 +0200 Subject: [PATCH 01/13] HammerOnPullOff implementation --- .../internal/NoteInput/NoteColorsSection.qml | 2 +- src/engraving/dom/chord.cpp | 27 ++ src/engraving/dom/chord.h | 2 + src/engraving/dom/dom.cmake | 2 + src/engraving/dom/edit.cpp | 108 ++++++- src/engraving/dom/engravingobject.cpp | 3 +- src/engraving/dom/engravingobject.h | 27 +- src/engraving/dom/factory.cpp | 5 + src/engraving/dom/factory.h | 3 + src/engraving/dom/hammeronpulloff.cpp | 281 ++++++++++++++++++ src/engraving/dom/hammeronpulloff.h | 118 ++++++++ src/engraving/dom/rest.cpp | 1 + src/engraving/dom/score.cpp | 2 + src/engraving/dom/score.h | 1 + src/engraving/dom/select.cpp | 1 + src/engraving/dom/selectionfilter.cpp | 2 + src/engraving/dom/slur.cpp | 84 +++--- src/engraving/dom/slur.h | 15 +- src/engraving/dom/slurtie.cpp | 11 + src/engraving/dom/slurtie.h | 4 +- src/engraving/dom/spanner.h | 2 +- src/engraving/dom/system.cpp | 1 + src/engraving/dom/undo.cpp | 5 +- src/engraving/infrastructure/shape.cpp | 24 ++ src/engraving/infrastructure/shape.h | 2 + .../rendering/editmode/editmoderenderer.cpp | 5 +- .../rendering/score/slurtielayout.cpp | 9 +- .../rendering/score/systemlayout.cpp | 1 + src/engraving/rendering/score/tdraw.cpp | 18 +- src/engraving/rendering/score/tdraw.h | 4 + src/engraving/rendering/score/tlayout.cpp | 54 +++- src/engraving/rendering/score/tlayout.h | 6 + src/engraving/rendering/single/singledraw.cpp | 15 + src/engraving/rendering/single/singledraw.h | 4 + .../rendering/single/singlelayout.cpp | 56 ++++ src/engraving/rendering/single/singlelayout.h | 2 + .../rw/read460/connectorinforeader.cpp | 1 + src/engraving/rw/read460/tread.cpp | 35 ++- src/engraving/rw/read460/tread.h | 4 + src/engraving/rw/write/twrite.cpp | 58 +++- src/engraving/rw/write/twrite.h | 8 + src/engraving/style/styledef.cpp | 20 ++ src/engraving/style/styledef.h | 20 ++ src/engraving/style/textstyle.cpp | 19 ++ src/engraving/types/types.h | 4 + src/engraving/types/typesconv.cpp | 7 + .../services/elementrepositoryservice.cpp | 2 + src/inspector/types/texttypes.h | 3 +- src/notation/inotationinteraction.h | 1 + .../internal/notationactioncontroller.cpp | 11 + .../internal/notationactioncontroller.h | 1 + src/notation/internal/notationinteraction.cpp | 14 + src/notation/internal/notationinteraction.h | 1 + src/notation/internal/notationuiactions.cpp | 6 + src/notation/notationmodule.cpp | 2 + src/notation/notationscene.qrc | 3 + .../EditStyle/HammerOnPullOffTappingPage.qml | 169 +++++++++++ .../hammerOnPullOffImages/hopoNotShowAll.png | Bin 0 -> 72633 bytes .../hammerOnPullOffImages/hopoShowAll.png | Bin 0 -> 82219 bytes .../tests/mocks/notationinteractionmock.h | 1 + .../hammeronpullofftappingpagemodel.cpp | 53 ++++ .../hammeronpullofftappingpagemodel.h | 45 +++ .../view/styledialog/styledialog.cmake | 2 + src/notation/view/widgets/editstyle.cpp | 21 +- src/notation/view/widgets/editstyle.ui | 18 +- src/notation/view/widgets/timeline.cpp | 1 + src/palette/internal/palettecreator.cpp | 10 +- 67 files changed, 1366 insertions(+), 81 deletions(-) create mode 100644 src/engraving/dom/hammeronpulloff.cpp create mode 100644 src/engraving/dom/hammeronpulloff.h create mode 100644 src/notation/qml/MuseScore/NotationScene/internal/EditStyle/HammerOnPullOffTappingPage.qml create mode 100644 src/notation/qml/MuseScore/NotationScene/internal/EditStyle/hammerOnPullOffImages/hopoNotShowAll.png create mode 100644 src/notation/qml/MuseScore/NotationScene/internal/EditStyle/hammerOnPullOffImages/hopoShowAll.png create mode 100644 src/notation/view/styledialog/hammeronpullofftappingpagemodel.cpp create mode 100644 src/notation/view/styledialog/hammeronpullofftappingpagemodel.h diff --git a/src/appshell/qml/Preferences/internal/NoteInput/NoteColorsSection.qml b/src/appshell/qml/Preferences/internal/NoteInput/NoteColorsSection.qml index 4178caa2766d4..de330eed55bcb 100644 --- a/src/appshell/qml/Preferences/internal/NoteInput/NoteColorsSection.qml +++ b/src/appshell/qml/Preferences/internal/NoteInput/NoteColorsSection.qml @@ -55,7 +55,7 @@ BaseSection { id: warnBendsBox width: parent.width - text: qsTrc("appshell/preferences", "Color guitar bends outside of playable range") + text: qsTrc("appshell/preferences", "Color guitar bends and hammer-ons/pull-offs outside of playable range") navigation.name: "WarnBendBox" navigation.panel: root.navigation diff --git a/src/engraving/dom/chord.cpp b/src/engraving/dom/chord.cpp index b370b56090439..37eb33ec05052 100644 --- a/src/engraving/dom/chord.cpp +++ b/src/engraving/dom/chord.cpp @@ -909,6 +909,33 @@ double Chord::centerX() const return x; } +bool Chord::allNotesTiedToNext() const +{ + Chord* tiedChord = nullptr; + for (Note* note : m_notes) { + if (!note->tieFor()) { + return false; + } + + Note* endNote = note->tieFor()->endNote(); + Chord* endChord = endNote ? endNote->chord() : nullptr; + if (!endChord) { + return false; + } + + if (!tiedChord) { + tiedChord = endChord; + continue; + } + + if (endChord != tiedChord) { + return false; + } + } + + return true; +} + //--------------------------------------------------------- // processSiblings //--------------------------------------------------------- diff --git a/src/engraving/dom/chord.h b/src/engraving/dom/chord.h index 48c6b3a58e703..1720c680bbc81 100644 --- a/src/engraving/dom/chord.h +++ b/src/engraving/dom/chord.h @@ -330,6 +330,8 @@ class Chord final : public ChordRest StartEndSlurs& startEndSlurs() { return m_startEndSlurs; } + bool allNotesTiedToNext() const; + private: friend class Factory; diff --git a/src/engraving/dom/dom.cmake b/src/engraving/dom/dom.cmake index 95561e8f79b2c..f4ed44fabe0b5 100644 --- a/src/engraving/dom/dom.cmake +++ b/src/engraving/dom/dom.cmake @@ -131,6 +131,8 @@ set(DOM_SRC ${CMAKE_CURRENT_LIST_DIR}/harmonicmark.h ${CMAKE_CURRENT_LIST_DIR}/harmony.cpp ${CMAKE_CURRENT_LIST_DIR}/harmony.h + ${CMAKE_CURRENT_LIST_DIR}/hammeronpulloff.cpp + ${CMAKE_CURRENT_LIST_DIR}/hammeronpulloff.h ${CMAKE_CURRENT_LIST_DIR}/harppedaldiagram.cpp ${CMAKE_CURRENT_LIST_DIR}/harppedaldiagram.h ${CMAKE_CURRENT_LIST_DIR}/hook.cpp diff --git a/src/engraving/dom/edit.cpp b/src/engraving/dom/edit.cpp index aa09a089be1fd..5c33f95f94e8c 100644 --- a/src/engraving/dom/edit.cpp +++ b/src/engraving/dom/edit.cpp @@ -44,6 +44,7 @@ #include "glissando.h" #include "guitarbend.h" #include "hairpin.h" +#include "hammeronpulloff.h" #include "harmony.h" #include "harppedaldiagram.h" #include "hook.h" @@ -667,8 +668,32 @@ Slur* Score::addSlur(ChordRest* firstChordRest, ChordRest* secondChordRest, cons options.disableOverRepeats = true; secondChordRest = nextChordRest(firstChordRest, options); - if (!secondChordRest) { - secondChordRest = firstChordRest; + if (!secondChordRest || !secondChordRest->isChord()) { + if (slurTemplate && slurTemplate->isHammerOnPullOff() && firstChordRest->isChord()) { + Note* endNote = GuitarBend::createEndNote(toChord(firstChordRest)->upNote()); + if (endNote) { + secondChordRest = endNote->chord(); + } + } + if (!secondChordRest) { + secondChordRest = firstChordRest; + } + } else if (secondChordRest->isChord()) { + bool firstChordRestIsTiedToSecond = firstChordRest->isChord() && toChord(firstChordRest)->allNotesTiedToNext() + && toChord(firstChordRest)->upNote()->tieFor()->endNote()->parent() == secondChordRest; + + // Follow chain of tied notes and slur until the last + while (toChord(secondChordRest)->allNotesTiedToNext()) { + secondChordRest = toChord(secondChordRest)->upNote()->tieFor()->endNote()->chord(); + } + + // If the first chord rest is also tied to this chain, slur to the next non-tied note + if (firstChordRestIsTiedToSecond) { + ChordRest* nextCandidate = nextChordRest(secondChordRest, options); + if (nextCandidate) { + secondChordRest = nextCandidate; + } + } } } @@ -687,7 +712,7 @@ Slur* Score::addSlur(ChordRest* firstChordRest, ChordRest* secondChordRest, cons slur->setEndElement(secondChordRest); firstChordRest->score()->undoAddElement(slur); - SlurSegment* ss = new SlurSegment(firstChordRest->score()->dummy()->system()); + SlurTieSegment* ss = slur->newSlurTieSegment(firstChordRest->score()->dummy()->system()); ss->setSpannerSegmentType(SpannerSegmentType::SINGLE); if (firstChordRest == secondChordRest && !(slur->isOutgoing() || slur->isIncoming())) { ss->setSlurOffset(Grip::END, PointF(3.0 * firstChordRest->style().spatium(), 0.0)); @@ -2603,6 +2628,7 @@ void Score::deleteItem(EngravingItem* el) case ElementType::KEYSIG: case ElementType::MEASURE_NUMBER: case ElementType::SYSTEM_LOCK_INDICATOR: + case ElementType::HAMMER_ON_PULL_OFF_TEXT: break; // All other types cannot be removed if generated default: @@ -3046,6 +3072,7 @@ void Score::deleteItem(EngravingItem* el) case ElementType::HARMONIC_MARK_SEGMENT: case ElementType::PICK_SCRAPE_SEGMENT: case ElementType::GUITAR_BEND_SEGMENT: + case ElementType::HAMMER_ON_PULL_OFF_SEGMENT: { el = toSpannerSegment(el)->spanner(); if (el->isTie()) { @@ -3061,6 +3088,10 @@ void Score::deleteItem(EngravingItem* el) } break; + case ElementType::HAMMER_ON_PULL_OFF_TEXT: + undoRemoveHopoText(toHammerOnPullOffText(el)); + break; + case ElementType::STEM_SLASH: // cannot delete this elements case ElementType::HOOK: case ElementType::GUITAR_BEND_TEXT: @@ -5138,7 +5169,7 @@ void Score::cloneVoice(track_idx_t strack, track_idx_t dtrack, Segment* sf, cons if (spanner) { // Find and add corresponding slurs and hairpins - static const std::set SPANNERS_TO_COPY { ElementType::SLUR, ElementType::HAIRPIN }; + static const std::set SPANNERS_TO_COPY { ElementType::SLUR, ElementType::HAMMER_ON_PULL_OFF, ElementType::HAIRPIN }; auto spanners = score->spannerMap().findOverlapping(start.ticks(), lTick.ticks()); for (auto i = spanners.begin(); i < spanners.end(); i++) { Spanner* sp = i->value; @@ -5996,6 +6027,7 @@ static void undoChangeNoteVisibility(Note* note, bool visible) ElementType::NOTE, ElementType::LYRICS, ElementType::SLUR, + ElementType::HAMMER_ON_PULL_OFF, ElementType::CHORD, // grace notes ElementType::LEDGER_LINE, // temporary objects, impossible to change visibility }; @@ -6284,6 +6316,7 @@ void Score::undoAddElement(EngravingItem* element, bool addToLinkedStaves, bool && et != ElementType::CHORDLINE && et != ElementType::LYRICS && et != ElementType::SLUR + && et != ElementType::HAMMER_ON_PULL_OFF && et != ElementType::TIE && et != ElementType::NOTE && et != ElementType::INSTRUMENT_CHANGE @@ -6355,6 +6388,7 @@ void Score::undoAddElement(EngravingItem* element, bool addToLinkedStaves, bool ElementType::OTTAVA, ElementType::TRILL, ElementType::SLUR, + ElementType::HAMMER_ON_PULL_OFF, ElementType::VIBRATO, ElementType::TEXTLINE, ElementType::PEDAL, @@ -7066,6 +7100,72 @@ void Score::undoRemoveElement(EngravingItem* element, bool removeLinked) } } +void Score::undoRemoveHopoText(HammerOnPullOffText* hopoText) +{ + Chord* startChord = hopoText->startChord(); + Chord* endChord = hopoText->endChord(); + IF_ASSERT_FAILED(startChord && endChord) { + return; + } + + HammerOnPullOffSegment* hopoSegment = toHammerOnPullOffSegment(hopoText->parentItem()); + HammerOnPullOff* hopo = hopoSegment ? hopoSegment->hammerOnPullOff() : nullptr; + IF_ASSERT_FAILED(hopo) { + return; + } + + Chord* hopoStartChord = toChord(hopo->startElement()); + Chord* hopoEndChord = toChord(hopo->endElement()); + IF_ASSERT_FAILED(hopoStartChord && hopoEndChord) { + return; + } + + if (startChord == hopoStartChord && endChord == hopoEndChord) { + undoRemoveElement(hopo); + return; + } + + Fraction hopoStartTick = hopo->tick(); + Fraction hopoEndTick = hopo->tick2(); + Fraction hopoTextStartTick = startChord->tick(); + Fraction hopoTextEndTick = endChord->tick(); + + bool shortenFromStart = (hopoTextStartTick - hopoStartTick) < (hopoEndTick - hopoTextEndTick); + EditData editData; + editData.curGrip = shortenFromStart ? Grip::START : Grip::END; + + if (shortenFromStart) { + Fraction newStartTick = hopoTextEndTick; + Fraction newTicks = hopoEndTick - newStartTick; + hopo->undoChangeProperty(Pid::SPANNER_TICK, newStartTick); + hopo->undoChangeProperty(Pid::SPANNER_TICKS, newTicks); + hopo->undoChangeStartEndElements(endChord, hopoEndChord); + if (startChord != hopoStartChord) { + HammerOnPullOff* newHopo = Factory::createHammerOnPullOff(score()->dummy()); + newHopo->setTrack(hopo->track()); + newHopo->setTick(hopoStartTick); + newHopo->setTick2(hopoTextStartTick); + newHopo->setStartElement(hopoStartChord); + newHopo->setEndElement(startChord); + score()->undoAddElement(newHopo); + } + } else { + Fraction newEndTick = hopoTextStartTick; + Fraction newTicks = newEndTick - hopoStartTick; + hopo->undoChangeProperty(Pid::SPANNER_TICKS, newTicks); + hopo->undoChangeStartEndElements(hopoStartChord, startChord); + if (endChord != hopoEndChord) { + HammerOnPullOff* newHopo = new HammerOnPullOff(score()->dummy()); + newHopo->setTrack(hopo->track()); + newHopo->setTick(hopoTextEndTick); + newHopo->setTick2(hopoEndTick); + newHopo->setStartElement(endChord); + newHopo->setEndElement(hopoEndChord); + score()->undoAddElement(newHopo); + } + } +} + //--------------------------------------------------------- // undoChangeSpannerElements //--------------------------------------------------------- diff --git a/src/engraving/dom/engravingobject.cpp b/src/engraving/dom/engravingobject.cpp index 4c38812778b29..baf721ad7d84b 100644 --- a/src/engraving/dom/engravingobject.cpp +++ b/src/engraving/dom/engravingobject.cpp @@ -757,7 +757,8 @@ bool EngravingObject::isTextBase() const || type() == ElementType::MMREST_RANGE || type() == ElementType::STICKING || type() == ElementType::HARP_DIAGRAM - || type() == ElementType::GUITAR_BEND_TEXT; + || type() == ElementType::GUITAR_BEND_TEXT + || type() == ElementType::HAMMER_ON_PULL_OFF_TEXT; } //--------------------------------------------------------- diff --git a/src/engraving/dom/engravingobject.h b/src/engraving/dom/engravingobject.h index 5cfe6d035beec..d7d6513aa84b8 100644 --- a/src/engraving/dom/engravingobject.h +++ b/src/engraving/dom/engravingobject.h @@ -84,6 +84,9 @@ class GuitarBendText; class HBox; class Hairpin; class HairpinSegment; +class HammerOnPullOff; +class HammerOnPullOffSegment; +class HammerOnPullOffText; class HarmonicMark; class HarmonicMarkSegment; class Harmony; @@ -346,7 +349,6 @@ class EngravingObject CONVERT(VBox, VBOX) CONVERT(TBox, TBOX) CONVERT(FBox, FBOX) - CONVERT(Slur, SLUR) CONVERT(Glissando, GLISSANDO) CONVERT(GlissandoSegment, GLISSANDO_SEGMENT) CONVERT(GuitarBend, GUITAR_BEND) @@ -370,7 +372,6 @@ class EngravingObject CONVERT(Beam, BEAM) CONVERT(Hook, HOOK) CONVERT(StemSlash, STEM_SLASH) - CONVERT(SlurSegment, SLUR_SEGMENT) CONVERT(LaissezVibSegment, LAISSEZ_VIB_SEGMENT) CONVERT(LaissezVib, LAISSEZ_VIB) CONVERT(PartialTieSegment, PARTIAL_TIE_SEGMENT) @@ -457,6 +458,9 @@ class EngravingObject CONVERT(TimeTickAnchor, TIME_TICK_ANCHOR) CONVERT(Parenthesis, PARENTHESIS) CONVERT(ShadowNote, SHADOW_NOTE) + CONVERT(HammerOnPullOff, HAMMER_ON_PULL_OFF) + CONVERT(HammerOnPullOffSegment, HAMMER_ON_PULL_OFF_SEGMENT) + CONVERT(HammerOnPullOffText, HAMMER_ON_PULL_OFF_TEXT) #undef CONVERT virtual bool isEngravingItem() const { return false; } // overridden in element.h @@ -486,6 +490,16 @@ class EngravingObject || isNoteLineSegment(); } + bool isSlur() const + { + return type() == ElementType::SLUR || type() == ElementType::HAMMER_ON_PULL_OFF; + } + + bool isSlurSegment() const + { + return type() == ElementType::SLUR_SEGMENT || type() == ElementType::HAMMER_ON_PULL_OFF_SEGMENT; + } + bool isLineSegment() const { return isGlissandoSegment() @@ -626,7 +640,8 @@ static inline SlurTieSegment* toSlurTieSegment(EngravingObject* e) { assert( e == 0 || e->type() == ElementType::SLUR_SEGMENT || e->type() == ElementType::TIE_SEGMENT - || e->type() == ElementType::LAISSEZ_VIB_SEGMENT || e->type() == ElementType::PARTIAL_TIE_SEGMENT); + || e->type() == ElementType::LAISSEZ_VIB_SEGMENT || e->type() == ElementType::PARTIAL_TIE_SEGMENT + || e->type() == ElementType::HAMMER_ON_PULL_OFF_SEGMENT); return (SlurTieSegment*)e; } @@ -634,7 +649,8 @@ static inline const SlurTieSegment* toSlurTieSegment(const EngravingObject* e) { assert( e == 0 || e->type() == ElementType::SLUR_SEGMENT || e->type() == ElementType::TIE_SEGMENT - || e->type() == ElementType::LAISSEZ_VIB_SEGMENT || e->type() == ElementType::PARTIAL_TIE_SEGMENT); + || e->type() == ElementType::LAISSEZ_VIB_SEGMENT || e->type() == ElementType::PARTIAL_TIE_SEGMENT + || e->type() == ElementType::HAMMER_ON_PULL_OFF_SEGMENT); return (const SlurTieSegment*)e; } @@ -877,5 +893,8 @@ CONVERT(PartialLyricsLine) CONVERT(PartialLyricsLineSegment) CONVERT(Parenthesis) CONVERT(ShadowNote) +CONVERT(HammerOnPullOff) +CONVERT(HammerOnPullOffSegment) +CONVERT(HammerOnPullOffText) #undef CONVERT } diff --git a/src/engraving/dom/factory.cpp b/src/engraving/dom/factory.cpp index f77ba6c8fde33..a46bd1e16d537 100644 --- a/src/engraving/dom/factory.cpp +++ b/src/engraving/dom/factory.cpp @@ -51,6 +51,7 @@ #include "gradualtempochange.h" #include "guitarbend.h" #include "hairpin.h" +#include "hammeronpulloff.h" #include "harmonicmark.h" #include "harmony.h" #include "harppedaldiagram.h" @@ -220,6 +221,7 @@ EngravingItem* Factory::doCreateItem(ElementType type, EngravingItem* parent) case ElementType::FIGURED_BASS: return new FiguredBass(parent->isSegment() ? toSegment(parent) : dummy->segment()); case ElementType::STEM: return new Stem(parent->isChord() ? toChord(parent) : dummy->chord()); case ElementType::SLUR: return new Slur(parent); + case ElementType::HAMMER_ON_PULL_OFF: return new HammerOnPullOff(parent); case ElementType::TIE: return new Tie(parent); case ElementType::TUPLET: return new Tuplet(parent->isMeasure() ? toMeasure(parent) : dummy->measure()); case ElementType::FINGERING: return new Fingering(parent->isNote() ? toNote(parent) : dummy->note()); @@ -661,6 +663,9 @@ COPY_ITEM_IMPL(Tuplet) CREATE_ITEM_IMPL(Hairpin, ElementType::HAIRPIN, EngravingItem, isAccessibleEnabled) MAKE_ITEM_IMPL(Hairpin, EngravingItem) +CREATE_ITEM_IMPL(HammerOnPullOff, ElementType::HAMMER_ON_PULL_OFF, EngravingItem, isAccessibleEnabled) +MAKE_ITEM_IMPL(HammerOnPullOff, EngravingItem) + CREATE_ITEM_IMPL(Glissando, ElementType::GLISSANDO, EngravingItem, isAccessibleEnabled) MAKE_ITEM_IMPL(Glissando, EngravingItem) diff --git a/src/engraving/dom/factory.h b/src/engraving/dom/factory.h index 7ddacc8ec7d4a..07660f8037182 100644 --- a/src/engraving/dom/factory.h +++ b/src/engraving/dom/factory.h @@ -241,6 +241,9 @@ class Factory static Hairpin* createHairpin(EngravingItem* parent, bool isAccessibleEnabled = true); static std::shared_ptr makeHairpin(EngravingItem* parent); + static HammerOnPullOff* createHammerOnPullOff(EngravingItem* parent, bool isAccessibleEnabled = true); + static std::shared_ptr makeHammerOnPullOff(EngravingItem* parent); + static Glissando* createGlissando(EngravingItem* parent, bool isAccessibleEnabled = true); static std::shared_ptr makeGlissando(EngravingItem* parent); diff --git a/src/engraving/dom/hammeronpulloff.cpp b/src/engraving/dom/hammeronpulloff.cpp new file mode 100644 index 0000000000000..01ae699562070 --- /dev/null +++ b/src/engraving/dom/hammeronpulloff.cpp @@ -0,0 +1,281 @@ +/* + * SPDX-License-Identifier: GPL-3.0-only + * MuseScore-Studio-CLA-applies + * + * MuseScore Studio + * Music Composition & Notation + * + * Copyright (C) 2021 MuseScore Limited + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "hammeronpulloff.h" +#include "note.h" +#include "score.h" +#include "stafftype.h" +#include "style/textstyle.h" +#include "system.h" + +namespace mu::engraving { +HammerOnPullOff::HammerOnPullOff(EngravingItem* parent) + : Slur(parent, ElementType::HAMMER_ON_PULL_OFF) +{ +} + +HammerOnPullOff::HammerOnPullOff(const HammerOnPullOff& other) + : Slur(other) +{ +} + +HammerOnPullOffSegment::HammerOnPullOffSegment(System* parent) + : SlurSegment(parent, ElementType::HAMMER_ON_PULL_OFF_SEGMENT) +{ +} + +HammerOnPullOffSegment::HammerOnPullOffSegment(const HammerOnPullOffSegment& other) + : SlurSegment(other) +{ +} + +Color HammerOnPullOffSegment::curColor() const +{ + if (score()->printing() || !MScore::warnGuitarBends || isValid()) { + return SlurSegment::curColor(); + } + + auto engravingConf = configuration(); + return selected() ? engravingConf->criticalSelectedColor() : engravingConf->criticalColor(); +} + +void HammerOnPullOffSegment::scanElements(void* data, void (*func)(void*, EngravingItem*), bool all) +{ + for (EngravingObject* child : scanChildren()) { + child->scanElements(data, func, all); + } + + func(data, this); +} + +EngravingObjectList HammerOnPullOffSegment::scanChildren() const +{ + EngravingObjectList children; + for (HammerOnPullOffText* hopo : m_hopoText) { + children.push_back(hopo); + } + + return children; +} + +void HammerOnPullOffSegment::setTrack(track_idx_t idx) +{ + m_track = idx; + for (HammerOnPullOffText* hopo : m_hopoText) { + hopo->setTrack(idx); + } +} + +void HammerOnPullOffSegment::updateHopoText() +{ + Chord* startChord = nullptr; + if (isSingleBeginType()) { + EngravingItem* startEl = hammerOnPullOff()->startElement(); + startChord = startEl && startEl->isChord() ? toChord(startEl) : nullptr; + } else { + ChordRest* firstCR = system()->firstChordRest(track()); + startChord = firstCR && firstCR->isChord() ? toChord(firstCR) : nullptr; + } + + Chord* endChord = nullptr; + if (isSingleEndType()) { + EngravingItem* endEl = hammerOnPullOff()->endElement(); + endChord = endEl && endEl->isChord() ? toChord(endEl) : nullptr; + } else { + ChordRest* lastCR = system()->lastChordRest(track()); + endChord = lastCR && lastCR->isChord() ? toChord(lastCR) : nullptr; + } + + if (!startChord || !endChord) { + muse::DeleteAll(m_hopoText); + m_hopoText.clear(); + return; + } + + std::vector hopoTextRegions = computeHopoTextRegions(startChord, endChord); + size_t regionCount = hopoTextRegions.size(); + + size_t curRegionIdx = 0; + for (; curRegionIdx < regionCount; ++curRegionIdx) { + HopoTextRegion curRegion = hopoTextRegions[curRegionIdx]; + + HammerOnPullOffText* curHopoText; + if (curRegionIdx < m_hopoText.size()) { + // Reuse existing, if available + curHopoText = m_hopoText[curRegionIdx]; + } else { + // Create new + curHopoText = new HammerOnPullOffText(this); + m_hopoText.push_back(curHopoText); + } + + curHopoText->setParent(this); + curHopoText->setTrack(track()); + curHopoText->setIsValid(curRegion.isValid); + curHopoText->setIsHammerOn(curRegion.isHammerOn); + curHopoText->setXmlText(style().styleB(Sid::hopoUpperCase) ? (curRegion.isHammerOn ? "H" : "P") : (curRegion.isHammerOn ? "h" : "p")); + curHopoText->setStartChord(curRegion.startChord); + curHopoText->setEndChord(curRegion.endChord); + } + + // Delete unused + if (curRegionIdx < m_hopoText.size()) { + size_t unusedHopoCount = m_hopoText.size() - curRegionIdx; + for (size_t i = 0; i < unusedHopoCount; ++i) { + delete m_hopoText.back(); + m_hopoText.pop_back(); + } + } +} + +std::vector HammerOnPullOffSegment::computeHopoTextRegions(Chord* startChord, Chord* endChord) +{ + std::vector result; + bool isTabStaff = staffType()->isTabStaff(); + if ((isTabStaff && !style().styleB(Sid::hopoShowOnTabStaves)) || (!isTabStaff && !style().styleB(Sid::hopoShowOnStandardStaves))) { + return result; + } + + for (Chord* curChord = startChord; curChord != endChord;) { + IF_ASSERT_FAILED(curChord->tick() <= endChord->tick()) { + break; + } + + Chord* nextChord = curChord->next(); + if (!nextChord) { + break; + } + + Note* curNote = curChord->upNote(); + Note* nextNote = nextChord->upNote(); + bool isValid = isTabStaff ? nextNote->string() == curNote->string() : nextNote->pitch() != curNote->pitch(); + bool isHammerOn = isTabStaff ? nextNote->fret() > curNote->fret() : nextNote->pitch() > curNote->pitch(); + + bool startNewRegion = result.empty() || style().styleB(Sid::hopoShowAll) || result.back().isHammerOn != isHammerOn; + + if (startNewRegion) { + HopoTextRegion region; + region.startChord = curChord; + region.endChord = nextChord; + region.isValid = isValid; + region.isHammerOn = isHammerOn; + result.push_back(region); + } else { + HopoTextRegion& curRegion = result.back(); + curRegion.endChord = nextChord; + curRegion.isValid = curRegion.isValid && isValid; + } + + curChord = nextChord; + } + + return result; +} + +bool HammerOnPullOffSegment::isUserModified() const +{ + for (const HammerOnPullOffText* hopoText : m_hopoText) { + if (hopoText->isUserModified()) { + return true; + } + } + + return SlurSegment::isUserModified(); +} + +bool HammerOnPullOffSegment::isValid() const +{ + for (const HammerOnPullOffText* hopoText : m_hopoText) { + if (!hopoText->isValid()) { + return false; + } + } + + return true; +} + +void HammerOnPullOffSegment::reset() +{ + for (HammerOnPullOffText* hopoText : m_hopoText) { + hopoText->reset(); + } + + SlurTieSegment::reset(); +} + +static ElementStyle hopoStyle; + +HammerOnPullOffText::HammerOnPullOffText(HammerOnPullOffSegment* parent) + : TextBase(ElementType::HAMMER_ON_PULL_OFF_TEXT, parent, TextStyleType::HAMMER_ON_PULL_OFF, + ElementFlag::MOVABLE | ElementFlag::GENERATED) +{ + initElementStyle(&hopoStyle); +} + +HammerOnPullOffText::HammerOnPullOffText(const HammerOnPullOffText& h) + : TextBase(h) +{ +} + +std::vector HammerOnPullOffText::dragAnchorLines() const +{ + std::vector result; + + PointF p1 = canvasPos(); + + HammerOnPullOffSegment* hopoSeg = toHammerOnPullOffSegment(parent()); + const Shape& hopoSegShape = hopoSeg->ldata()->shape(); + double x = ldata()->pos().x();// + hopoSegShape.bbox().x(); + double y = hopoSeg->hammerOnPullOff()->up() ? hopoSegShape.topAtX(x) : hopoSegShape.bottomAtX(x); + + PointF p2 = PointF(x, y) + hopoSeg->canvasPos(); + + result.push_back(LineF(p1, p2)); + + return result; +} + +bool HammerOnPullOffText::isUserModified() const +{ + for (const TextStyleProperty& p : *textStyle(textStyleType())) { + if (getProperty(p.pid) != propertyDefault(p.pid)) { + return true; + } + } + + return TextBase::isUserModified(); +} + +Color HammerOnPullOffText::curColor() const +{ + if (score()->printing()) { + return TextBase::curColor(); + } + + auto engravingConf = configuration(); + if (isValid() || !MScore::warnGuitarBends) { + return selected() || (parentItem() && parentItem()->selected()) ? engravingConf->selectionColor() : TextBase::curColor(); + } + + return selected() || parentItem()->selected() ? engravingConf->criticalSelectedColor() : engravingConf->criticalColor(); +} +} // namespace mu::engraving diff --git a/src/engraving/dom/hammeronpulloff.h b/src/engraving/dom/hammeronpulloff.h new file mode 100644 index 0000000000000..9a871173dc16d --- /dev/null +++ b/src/engraving/dom/hammeronpulloff.h @@ -0,0 +1,118 @@ +/* + * SPDX-License-Identifier: GPL-3.0-only + * MuseScore-Studio-CLA-applies + * + * MuseScore Studio + * Music Composition & Notation + * + * Copyright (C) 2021 MuseScore Limited + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include "slur.h" +#include "textbase.h" + +namespace mu::engraving { +class HammerOnPullOffText final : public TextBase +{ + OBJECT_ALLOCATOR(engraving, HammerOnPullOffText) + DECLARE_CLASSOF(ElementType::HAMMER_ON_PULL_OFF_TEXT) + +public: + HammerOnPullOffText(HammerOnPullOffSegment* parent = nullptr); + HammerOnPullOffText(const HammerOnPullOffText& h); + HammerOnPullOffText* clone() const override { return new HammerOnPullOffText(*this); } + + bool isEditAllowed(EditData&) const override { return false; } + + Chord* startChord() const { return m_startChord; } + Chord* endChord() const { return m_endChord; } + void setStartChord(Chord* c) { m_startChord = c; } + void setEndChord(Chord* c) { m_endChord = c; } + + std::vector dragAnchorLines() const override; + + bool isUserModified() const override; + bool isValid() const { return m_isValid; } + void setIsValid(bool v) { m_isValid = v; } + bool isHammerOn() const { return m_isHammerOn; } + void setIsHammerOn(bool v) { m_isHammerOn = v; } + + Color curColor() const override; + +private: + Chord* m_startChord = nullptr; + Chord* m_endChord = nullptr; + bool m_isValid = true; + bool m_isHammerOn = true; +}; + +class HammerOnPullOffSegment final : public SlurSegment +{ + OBJECT_ALLOCATOR(engraving, HammerOnPullOffSegment) + DECLARE_CLASSOF(ElementType::HAMMER_ON_PULL_OFF_SEGMENT) + +public: + HammerOnPullOffSegment(System* parent); + HammerOnPullOffSegment(const HammerOnPullOffSegment& other); + + HammerOnPullOffSegment* clone() const override { return new HammerOnPullOffSegment(*this); } + + HammerOnPullOff* hammerOnPullOff() const { return toHammerOnPullOff(spanner()); } + + Color curColor() const override; + + void scanElements(void* data, void (* func)(void*, EngravingItem*), bool all=true) override; + EngravingObjectList scanChildren() const override; + + void setTrack(track_idx_t idx) override; + + void updateHopoText(); + void addHopoText(HammerOnPullOffText* t) { m_hopoText.push_back(t); } + const std::vector& hopoText() const { return m_hopoText; } + + bool isUserModified() const override; + bool isValid() const; + + void reset() override; + +private: + struct HopoTextRegion { + Chord* startChord = nullptr; + Chord* endChord = nullptr; + bool isHammerOn = true; + bool isValid = true; + }; + + std::vector computeHopoTextRegions(Chord* startChord, Chord* endChord); + + std::vector m_hopoText; +}; + +class HammerOnPullOff final : public Slur +{ + OBJECT_ALLOCATOR(engraving, HammerOnPullOff) + DECLARE_CLASSOF(ElementType::HAMMER_ON_PULL_OFF) + +public: + HammerOnPullOff* clone() const override { return new HammerOnPullOff(*this); } + SlurTieSegment* newSlurTieSegment(System* parent) override { return new HammerOnPullOffSegment(parent); } + + friend class Factory; + + HammerOnPullOff(EngravingItem* parent); + HammerOnPullOff(const HammerOnPullOff&); +}; +} // namespace mu::engraving diff --git a/src/engraving/dom/rest.cpp b/src/engraving/dom/rest.cpp index 70035e061fd3e..0a456c15b272e 100644 --- a/src/engraving/dom/rest.cpp +++ b/src/engraving/dom/rest.cpp @@ -219,6 +219,7 @@ bool Rest::acceptDrop(EditData& data) const // prevent 'hanging' slurs, avoid crash on tie static const std::set ignoredTypes { ElementType::SLUR, + ElementType::HAMMER_ON_PULL_OFF, ElementType::TIE, ElementType::GLISSANDO }; diff --git a/src/engraving/dom/score.cpp b/src/engraving/dom/score.cpp index ff2fd62c6d64b..b1c815c6ef0e0 100644 --- a/src/engraving/dom/score.cpp +++ b/src/engraving/dom/score.cpp @@ -1528,6 +1528,7 @@ void Score::addElement(EngravingItem* element) break; case ElementType::SLUR: + case ElementType::HAMMER_ON_PULL_OFF: addLayoutFlags(LayoutFlag::PLAY_EVENTS); // fall through @@ -1722,6 +1723,7 @@ void Score::removeElement(EngravingItem* element) break; case ElementType::SLUR: + case ElementType::HAMMER_ON_PULL_OFF: addLayoutFlags(LayoutFlag::PLAY_EVENTS); // fall through diff --git a/src/engraving/dom/score.h b/src/engraving/dom/score.h index cdc3c765ea35b..5e129912047a6 100644 --- a/src/engraving/dom/score.h +++ b/src/engraving/dom/score.h @@ -457,6 +457,7 @@ class Score : public EngravingObject, public muse::Injectable EngravingItem* elementToRelink = nullptr); void undoAddCR(ChordRest* element, Measure*, const Fraction& tick); void undoRemoveElement(EngravingItem* element, bool removeLinked = true); + void undoRemoveHopoText(HammerOnPullOffText* hopoText); void undoChangeSpannerElements(Spanner* spanner, EngravingItem* startElement, EngravingItem* endElement); void undoChangeElement(EngravingItem* oldElement, EngravingItem* newElement); void undoChangePitch(Note* note, int pitch, int tpc1, int tpc2); diff --git a/src/engraving/dom/select.cpp b/src/engraving/dom/select.cpp index 97b2879b48b57..34bf704486f44 100644 --- a/src/engraving/dom/select.cpp +++ b/src/engraving/dom/select.cpp @@ -1075,6 +1075,7 @@ muse::ByteArray Selection::symbolListMimeData() const e = toSpannerSegment(e)->spanner(); [[fallthrough]]; case ElementType::SLUR: + case ElementType::HAMMER_ON_PULL_OFF: case ElementType::HAIRPIN: case ElementType::OTTAVA: case ElementType::TRILL: diff --git a/src/engraving/dom/selectionfilter.cpp b/src/engraving/dom/selectionfilter.cpp index b9ba6ab335d95..ff26d41556928 100644 --- a/src/engraving/dom/selectionfilter.cpp +++ b/src/engraving/dom/selectionfilter.cpp @@ -134,6 +134,8 @@ bool SelectionFilter::canSelect(const EngravingItem* e) const return isFiltered(ElementsSelectionFilterTypes::CHORD_SYMBOL); case ElementType::SLUR: case ElementType::SLUR_SEGMENT: + case ElementType::HAMMER_ON_PULL_OFF: + case ElementType::HAMMER_ON_PULL_OFF_SEGMENT: return isFiltered(ElementsSelectionFilterTypes::SLUR); case ElementType::FIGURED_BASS: return isFiltered(ElementsSelectionFilterTypes::FIGURED_BASS); diff --git a/src/engraving/dom/slur.cpp b/src/engraving/dom/slur.cpp index cb67cd407ef75..42215bb9fad02 100644 --- a/src/engraving/dom/slur.cpp +++ b/src/engraving/dom/slur.cpp @@ -46,8 +46,8 @@ using namespace mu::engraving; using namespace muse::draw; namespace mu::engraving { -SlurSegment::SlurSegment(System* parent) - : SlurTieSegment(ElementType::SLUR_SEGMENT, parent) +SlurSegment::SlurSegment(System* parent, ElementType type) + : SlurTieSegment(type, parent) { } @@ -251,39 +251,7 @@ void SlurSegment::changeAnchor(EditData& ed, EngravingItem* element) } // update start/end elements (which could be grace notes) - for (EngravingObject* lsp : spanner()->linkList()) { - Spanner* sp = static_cast(lsp); - if (sp == spanner()) { - score()->undo(new ChangeSpannerElements(sp, scr, ecr)); - } else { - EngravingItem* se = 0; - EngravingItem* ee = 0; - if (scr) { - std::list sel = scr->linkList(); - for (EngravingObject* lcr : sel) { - EngravingItem* le = toEngravingItem(lcr); - if (le->score() == sp->score() && le->track() == sp->track()) { - se = le; - break; - } - } - } - if (ecr) { - std::list sel = ecr->linkList(); - for (EngravingObject* lcr : sel) { - EngravingItem* le = toEngravingItem(lcr); - if (le->score() == sp->score() && le->track() == sp->track2()) { - ee = le; - break; - } - } - } - if (se && ee) { - score()->undo(new ChangeStartEndSpanner(sp, se, ee)); - renderer()->layoutItem(sp); - } - } - } + slur()->undoChangeStartEndElements(scr, ecr); const size_t segments = spanner()->spannerSegments().size(); ups(ed.curGrip).off = PointF(); @@ -390,6 +358,11 @@ double SlurSegment::dottedWidth() const return style().styleMM(Sid::slurDottedWidth); } +Color SlurSegment::curColor() const +{ + return EngravingItem::curColor(getProperty(Pid::VISIBLE).toBool(), getProperty(Pid::COLOR).value()); +} + Slur::Slur(const Slur& s) : SlurTie(s) { @@ -401,8 +374,8 @@ Slur::Slur(const Slur& s) // Slur //--------------------------------------------------------- -Slur::Slur(EngravingItem* parent) - : SlurTie(ElementType::SLUR, parent) +Slur::Slur(EngravingItem* parent, ElementType type) + : SlurTie(type, parent) { setAnchor(Anchor::CHORD); } @@ -509,6 +482,43 @@ bool Slur::isOutgoing() const return _partialSpannerDirection == PartialSpannerDirection::BOTH || _partialSpannerDirection == PartialSpannerDirection::OUTGOING; } +void Slur::undoChangeStartEndElements(ChordRest* scr, ChordRest* ecr) +{ + for (EngravingObject* lsp : linkList()) { + Spanner* sp = static_cast(lsp); + if (sp == this) { + score()->undo(new ChangeSpannerElements(this, scr, ecr)); + } else { + EngravingItem* se = 0; + EngravingItem* ee = 0; + if (scr) { + std::list sel = scr->linkList(); + for (EngravingObject* lcr : sel) { + EngravingItem* le = toEngravingItem(lcr); + if (le->score() == sp->score() && le->track() == sp->track()) { + se = le; + break; + } + } + } + if (ecr) { + std::list sel = ecr->linkList(); + for (EngravingObject* lcr : sel) { + EngravingItem* le = toEngravingItem(lcr); + if (le->score() == sp->score() && le->track() == sp->track2()) { + ee = le; + break; + } + } + } + if (se && ee) { + score()->undo(new ChangeStartEndSpanner(sp, se, ee)); + renderer()->layoutItem(sp); + } + } + } +} + //--------------------------------------------------------- // setTrack //--------------------------------------------------------- diff --git a/src/engraving/dom/slur.h b/src/engraving/dom/slur.h index f903fe7824d4b..dd527557319ad 100644 --- a/src/engraving/dom/slur.h +++ b/src/engraving/dom/slur.h @@ -32,7 +32,7 @@ namespace mu::engraving { /// a single segment of slur; also used for Tie //--------------------------------------------------------- -class SlurSegment final : public SlurTieSegment +class SlurSegment : public SlurTieSegment { OBJECT_ALLOCATOR(engraving, SlurSegment) DECLARE_CLASSOF(ElementType::SLUR_SEGMENT) @@ -42,7 +42,7 @@ class SlurSegment final : public SlurTieSegment M_PROPERTY2(PointF, endPointOff2, setEndPointOff2, PointF(0.0, 0.0)) public: - SlurSegment(System* parent); + SlurSegment(System* parent, ElementType type = ElementType::SLUR_SEGMENT); SlurSegment(const SlurSegment& ss); SlurSegment* clone() const override { return new SlurSegment(*this); } @@ -60,6 +60,8 @@ class SlurSegment final : public SlurTieSegment double midWidth() const override; double dottedWidth() const override; + Color curColor() const override; + protected: void changeAnchor(EditData&, EngravingItem*) override; }; @@ -68,12 +70,14 @@ class SlurSegment final : public SlurTieSegment // @@ Slur //--------------------------------------------------------- -class Slur final : public SlurTie +class Slur : public SlurTie { OBJECT_ALLOCATOR(engraving, Slur) DECLARE_CLASSOF(ElementType::SLUR) public: + Slur(EngravingItem* parent, ElementType type = ElementType::SLUR); + Slur(const Slur&); struct StemFloated { @@ -124,6 +128,9 @@ class Slur final : public SlurTie void setOutgoing(bool outgoing); bool isIncoming() const; bool isOutgoing() const; + + void undoChangeStartEndElements(ChordRest* scr, ChordRest* ecr); + private: M_PROPERTY2(ConnectedElement, connectedElement, setConnectedElement, ConnectedElement::NONE) M_PROPERTY2(PartialSpannerDirection, partialSpannerDirection, setPartialSpannerDirection, PartialSpannerDirection::NONE) @@ -132,8 +139,6 @@ class Slur final : public SlurTie PartialSpannerDirection calcOutgoingDirection(bool outgoing); friend class Factory; - Slur(EngravingItem* parent); - Slur(const Slur&); StemFloated m_stemFloated; // end point position is attached to stem but floated towards the note }; diff --git a/src/engraving/dom/slurtie.cpp b/src/engraving/dom/slurtie.cpp index 088bdd31423fa..c13bb2600c770 100644 --- a/src/engraving/dom/slurtie.cpp +++ b/src/engraving/dom/slurtie.cpp @@ -180,6 +180,17 @@ std::vector SlurTieSegment::gripsPositions(const EditData&) const return grips; } +bool SlurTieSegment::isUserModified() const +{ + return SpannerSegment::isUserModified() || !(visible() && autoplace() + && color() == configuration()->defaultColor() + && offset().isNull() + && ups(Grip::START).off.isNull() + && ups(Grip::BEZIER1).off.isNull() + && ups(Grip::BEZIER2).off.isNull() + && ups(Grip::END).off.isNull()); +} + //--------------------------------------------------------- // startEditDrag //--------------------------------------------------------- diff --git a/src/engraving/dom/slurtie.h b/src/engraving/dom/slurtie.h index 12935f0e0bd0c..af1d987eac092 100644 --- a/src/engraving/dom/slurtie.h +++ b/src/engraving/dom/slurtie.h @@ -105,7 +105,7 @@ class SlurTieSegment : public SpannerSegment PropertyValue getProperty(Pid propertyId) const override; bool setProperty(Pid propertyId, const PropertyValue&) override; PropertyValue propertyDefault(Pid id) const override; - void reset() override; + virtual void reset() override; void undoChangeProperty(Pid id, const PropertyValue&, PropertyFlags ps) override; void move(const PointF& s) override; bool isEditable() const override { return true; } @@ -123,6 +123,8 @@ class SlurTieSegment : public SpannerSegment Grip defaultGrip() const override { return Grip::DRAG; } std::vector gripsPositions(const EditData& = EditData()) const override; + virtual bool isUserModified() const override; + virtual double endWidth() const = 0; virtual double midWidth() const = 0; virtual double dottedWidth() const = 0; diff --git a/src/engraving/dom/spanner.h b/src/engraving/dom/spanner.h index aab654601696e..10ffe10193dba 100644 --- a/src/engraving/dom/spanner.h +++ b/src/engraving/dom/spanner.h @@ -118,7 +118,7 @@ class SpannerSegment : public EngravingItem std::list linkListForPropertyPropagation() const override; bool isPropertyLinkedToMaster(Pid id) const override; - bool isUserModified() const override; + virtual bool isUserModified() const override; bool allowTimeAnchor() const override; diff --git a/src/engraving/dom/system.cpp b/src/engraving/dom/system.cpp index db3b7df5173d5..a5b176ce59b75 100644 --- a/src/engraving/dom/system.cpp +++ b/src/engraving/dom/system.cpp @@ -489,6 +489,7 @@ void System::add(EngravingItem* el) case ElementType::PICK_SCRAPE_SEGMENT: case ElementType::GUITAR_BEND_SEGMENT: case ElementType::GUITAR_BEND_HOLD_SEGMENT: + case ElementType::HAMMER_ON_PULL_OFF_SEGMENT: { SpannerSegment* ss = toSpannerSegment(el); #ifndef NDEBUG diff --git a/src/engraving/dom/undo.cpp b/src/engraving/dom/undo.cpp index 62f4bb3fd9c3c..c9c90eedea95d 100644 --- a/src/engraving/dom/undo.cpp +++ b/src/engraving/dom/undo.cpp @@ -557,8 +557,9 @@ void UndoStack::redo(EditData* ed) bool UndoMacro::canRecordSelectedElement(const EngravingItem* e) { - return e->isNote() || (e->isChordRest() && !e->isChord()) || (e->isTextBase() && !e->isInstrumentName()) || e->isFretDiagram() - || e->isSoundFlag(); + return e->isNote() || (e->isChordRest() && !e->isChord()) + || (e->isTextBase() && !e->isInstrumentName() && !e->isHammerOnPullOffText()) + || e->isFretDiagram() || e->isSoundFlag(); } void UndoMacro::fillSelectionInfo(SelectionInfo& info, const Selection& sel) diff --git a/src/engraving/infrastructure/shape.cpp b/src/engraving/infrastructure/shape.cpp index 86a675452befe..856bceeadb6b0 100644 --- a/src/engraving/infrastructure/shape.cpp +++ b/src/engraving/infrastructure/shape.cpp @@ -368,6 +368,30 @@ double Shape::bottom() const return dist; } +double Shape::topAtX(double x) const +{ + double localTop = DBL_MAX; + for (const ShapeElement& el : m_elements) { + if (el.left() < x && el.right() > x) { + localTop = std::min(localTop, el.top()); + } + } + + return localTop != DBL_MAX ? localTop : top(); +} + +double Shape::bottomAtX(double x) const +{ + double localBottom = -DBL_MAX; + for (const ShapeElement& el : m_elements) { + if (el.left() < x && el.right() > x) { + localBottom = std::max(localBottom, el.bottom()); + } + } + + return localBottom != DBL_MAX ? localBottom : bottom(); +} + double Shape::rightMostEdgeAtHeight(double yAbove, double yBelow) const { double edge = -DBL_MAX; diff --git a/src/engraving/infrastructure/shape.h b/src/engraving/infrastructure/shape.h index 07f22780c2e7a..1177f836fc91f 100644 --- a/src/engraving/infrastructure/shape.h +++ b/src/engraving/infrastructure/shape.h @@ -159,6 +159,8 @@ class Shape double right() const; double top() const; double bottom() const; + double topAtX(double x) const; + double bottomAtX(double x) const; double rightMostEdgeAtHeight(double yAbove, double yBelow) const; double leftMostEdgeAtHeight(double yAbove, double yBelow) const; double leftMostEdgeAtTop() const; diff --git a/src/engraving/rendering/editmode/editmoderenderer.cpp b/src/engraving/rendering/editmode/editmoderenderer.cpp index 2b2d1234b4d4c..5845072248113 100644 --- a/src/engraving/rendering/editmode/editmoderenderer.cpp +++ b/src/engraving/rendering/editmode/editmoderenderer.cpp @@ -99,6 +99,8 @@ void EditModeRenderer::drawItem(EngravingItem* item, muse::draw::Painter* painte drawTextBase(item_cast(item), painter, ed, currentViewScaling); break; case ElementType::SLUR_SEGMENT: + case ElementType::TIE_SEGMENT: + case ElementType::HAMMER_ON_PULL_OFF_SEGMENT: drawSlurTieSegment(item_cast(item), painter, ed, currentViewScaling); break; case ElementType::STAFF_TEXT: @@ -119,9 +121,6 @@ void EditModeRenderer::drawItem(EngravingItem* item, muse::draw::Painter* painte case ElementType::TEXT: drawTextBase(item_cast(item), painter, ed, currentViewScaling); break; - case ElementType::TIE_SEGMENT: - drawSlurTieSegment(item_cast(item), painter, ed, currentViewScaling); - break; case ElementType::TRIPLET_FEEL: drawTextBase(item_cast(item), painter, ed, currentViewScaling); break; diff --git a/src/engraving/rendering/score/slurtielayout.cpp b/src/engraving/rendering/score/slurtielayout.cpp index 172fd2d3a8ca2..cdd003e9e4c00 100644 --- a/src/engraving/rendering/score/slurtielayout.cpp +++ b/src/engraving/rendering/score/slurtielayout.cpp @@ -44,6 +44,7 @@ #include "dom/laissezvib.h" #include "dom/parenthesis.h" #include "dom/partialtie.h" +#include "dom/hammeronpulloff.h" #include "tlayout.h" #include "chordlayout.h" @@ -65,8 +66,8 @@ SpannerSegment* SlurTieLayout::layoutSystem(Slur* item, System* system, LayoutCo Fraction stick = system->firstMeasure()->tick(); Fraction etick = system->lastMeasure()->endTick(); - SlurSegment* slurSegment = toSlurSegment(TLayout::getNextLayoutSystemSegment(item, system, [](System* parent) { - return new SlurSegment(parent); + SlurSegment* slurSegment = toSlurSegment(TLayout::getNextLayoutSystemSegment(item, system, [item](System* parent) { + return item->newSlurTieSegment(parent); })); SpannerSegmentType sst; @@ -3007,6 +3008,10 @@ void SlurTieLayout::layoutSegment(SlurSegment* item, LayoutContext& ctx, const P ldata->moveY(item->staffOffsetY()); computeBezier(item); + + if (item->isHammerOnPullOffSegment()) { + TLayout::layoutHammerOnPullOffSegment(toHammerOnPullOffSegment(item), ctx); + } } void SlurTieLayout::computeMidThickness(SlurTieSegment* slurTieSeg, double slurTieLengthInSp) diff --git a/src/engraving/rendering/score/systemlayout.cpp b/src/engraving/rendering/score/systemlayout.cpp index 8baa177a60ff7..1907861b47772 100644 --- a/src/engraving/rendering/score/systemlayout.cpp +++ b/src/engraving/rendering/score/systemlayout.cpp @@ -1238,6 +1238,7 @@ void SystemLayout::collectSpannersToLayout(ElementsToLayout& elements, const Lay } else { switch (spanner->type()) { case ElementType::SLUR: + case ElementType::HAMMER_ON_PULL_OFF: if (!toSlur(spanner)->isCrossStaff()) { elements.slurs.push_back(spanner); } diff --git a/src/engraving/rendering/score/tdraw.cpp b/src/engraving/rendering/score/tdraw.cpp index a40a6abbeb8a3..3356cc0c119c5 100644 --- a/src/engraving/rendering/score/tdraw.cpp +++ b/src/engraving/rendering/score/tdraw.cpp @@ -63,6 +63,7 @@ #include "dom/guitarbend.h" #include "dom/hairpin.h" +#include "dom/hammeronpulloff.h" #include "dom/harppedaldiagram.h" #include "dom/harmonicmark.h" #include "dom/harmony.h" @@ -243,6 +244,10 @@ void TDraw::drawItem(const EngravingItem* item, Painter* painter) case ElementType::HAIRPIN_SEGMENT: draw(item_cast(item), painter); break; + case ElementType::HAMMER_ON_PULL_OFF_SEGMENT: draw(item_cast(item), painter); + break; + case ElementType::HAMMER_ON_PULL_OFF_TEXT: draw(item_cast(item), painter); + break; case ElementType::HARP_DIAGRAM: draw(item_cast(item), painter); break; case ElementType::HARMONIC_MARK_SEGMENT: draw(item_cast(item), painter); @@ -1873,6 +1878,17 @@ void TDraw::draw(const HairpinSegment* item, Painter* painter) } } +void TDraw::draw(const HammerOnPullOffSegment* item, muse::draw::Painter* painter) +{ + draw(toSlurSegment(item), painter); +} + +void TDraw::draw(const HammerOnPullOffText* item, muse::draw::Painter* painter) +{ + TRACE_DRAW_ITEM; + drawTextBase(item, painter); +} + void TDraw::draw(const HarpPedalDiagram* item, Painter* painter) { TRACE_DRAW_ITEM; @@ -2609,7 +2625,7 @@ void TDraw::draw(const SlurSegment* item, Painter* painter) { TRACE_DRAW_ITEM; - Pen pen(item->curColor(item->getProperty(Pid::VISIBLE).toBool(), item->getProperty(Pid::COLOR).value())); + Pen pen(item->curColor()); double mag = item->staff() ? item->staff()->staffMag(item->slur()->tick()) : 1.0; //Replace generic Qt dash patterns with improved equivalents to show true dots (keep in sync with tie.cpp) diff --git a/src/engraving/rendering/score/tdraw.h b/src/engraving/rendering/score/tdraw.h index 00f3a8a3dbf08..c287d8af3034b 100644 --- a/src/engraving/rendering/score/tdraw.h +++ b/src/engraving/rendering/score/tdraw.h @@ -69,6 +69,8 @@ class GradualTempoChange; class HairpinSegment; class Hairpin; +class HammerOnPullOffSegment; +class HammerOnPullOffText; class HarpPedalDiagram; class HarmonicMarkSegment; class Harmony; @@ -225,6 +227,8 @@ class TDraw static void draw(const GuitarBendHoldSegment* item, muse::draw::Painter* painter); static void draw(const HairpinSegment* item, muse::draw::Painter* painter); + static void draw(const HammerOnPullOffSegment* item, muse::draw::Painter* painter); + static void draw(const HammerOnPullOffText* item, muse::draw::Painter* painter); static void draw(const HarpPedalDiagram* item, muse::draw::Painter* painter); static void draw(const HarmonicMarkSegment* item, muse::draw::Painter* painter); static void draw(const Harmony* item, muse::draw::Painter* painter); diff --git a/src/engraving/rendering/score/tlayout.cpp b/src/engraving/rendering/score/tlayout.cpp index 5a09165add430..c4a6dbbc85e16 100644 --- a/src/engraving/rendering/score/tlayout.cpp +++ b/src/engraving/rendering/score/tlayout.cpp @@ -69,6 +69,7 @@ #include "dom/guitarbend.h" #include "dom/hairpin.h" +#include "dom/hammeronpulloff.h" #include "dom/harppedaldiagram.h" #include "dom/harmonicmark.h" #include "dom/harmony.h" @@ -276,6 +277,12 @@ void TLayout::layoutItem(EngravingItem* item, LayoutContext& ctx) break; case ElementType::HAIRPIN_SEGMENT: layoutHairpinSegment(item_cast(item), ctx); break; + case ElementType::HAMMER_ON_PULL_OFF: layoutHammerOnPullOff(item_cast(item), ctx); + break; + case ElementType::HAMMER_ON_PULL_OFF_SEGMENT: layoutHammerOnPullOffSegment(item_cast(item), ctx); + break; + case ElementType::HAMMER_ON_PULL_OFF_TEXT: layoutHammerOnPullOffText(item_cast(item), ctx); + break; case ElementType::HARP_DIAGRAM: layoutHarpPedalDiagram(item_cast(item), static_cast(ldata)); break; @@ -3242,6 +3249,51 @@ void TLayout::layoutHairpin(Hairpin* item, LayoutContext& ctx) layoutTextLineBase(item, ctx); } +void TLayout::layoutHammerOnPullOff(HammerOnPullOff* item, LayoutContext& ctx) +{ + layoutSlur(static_cast(item), ctx); +} + +void TLayout::layoutHammerOnPullOffSegment(HammerOnPullOffSegment* item, LayoutContext& ctx) +{ + // The layout of the slur has already been done. Here we layout the H/P letters. + item->updateHopoText(); + + HammerOnPullOff* hopo = item->hammerOnPullOff(); + Shape hopoSegmentShape = item->mutldata()->shape(); + + for (HammerOnPullOffText* hopoText : item->hopoText()) { + Align align; + align.vertical = hopo->up() ? AlignV::BASELINE : AlignV::TOP; + align.horizontal = AlignH::HCENTER; + hopoText->setAlign(align); + layoutItem(hopoText, ctx); + + double startX = hopoText->startChord()->systemPos().x() + hopoText->startChord()->upNote()->headWidth(); + double endX = hopoText->endChord()->systemPos().x(); + double centerX = 0.5 * (startX + endX); + + double vertPadding = 0.5 * item->spatium(); + Shape hopoTextShape = hopoText->ldata()->shape().translated(PointF(centerX, 0.0)); + double y = hopo->up() ? -hopoTextShape.minVerticalDistance(hopoSegmentShape) : hopoSegmentShape.minVerticalDistance(hopoTextShape); + y += hopo->up() ? -vertPadding : vertPadding; + y = hopo->up() ? std::min(y, -vertPadding) : std::max(y, item->staff()->staffHeight(item->tick()) + vertPadding); + + hopoText->mutldata()->setPos(centerX, y); + } + + for (HammerOnPullOffText* hopoText : item->hopoText()) { + hopoSegmentShape.add(hopoText->ldata()->shape().translated(hopoText->pos())); + } + + item->mutldata()->setShape(hopoSegmentShape); +} + +void TLayout::layoutHammerOnPullOffText(HammerOnPullOffText* item, LayoutContext& ctx) +{ + layoutBaseTextBase(item, ctx); +} + void TLayout::fillHairpinSegmentShape(const HairpinSegment* item, HairpinSegment::LayoutData* ldata) { LAYOUT_CALL_ITEM(item); @@ -7025,7 +7077,7 @@ void TLayout::layoutWhammyBarSegment(WhammyBarSegment* item, LayoutContext& ctx) Autoplace::autoplaceSpannerSegment(item, ldata, ctx.conf().spatium()); } -using LayoutSystemTypes = rtti::TypeList; +using LayoutSystemTypes = rtti::TypeList; class LayoutSystemVisitor : public rtti::Visitor { diff --git a/src/engraving/rendering/score/tlayout.h b/src/engraving/rendering/score/tlayout.h index b9284c2ff752b..43dcced97cd96 100644 --- a/src/engraving/rendering/score/tlayout.h +++ b/src/engraving/rendering/score/tlayout.h @@ -122,6 +122,9 @@ class GradualTempoChange; class HairpinSegment; class Hairpin; +class HammerOnPullOff; +class HammerOnPullOffSegment; +class HammerOnPullOffText; class HarmonicMarkSegment; class LedgerLine; @@ -247,6 +250,9 @@ class TLayout static void layoutHairpinSegment(HairpinSegment* item, LayoutContext& ctx); static void layoutHairpin(Hairpin* item, LayoutContext& ctx); + static void layoutHammerOnPullOff(HammerOnPullOff* item, LayoutContext& ctx); + static void layoutHammerOnPullOffSegment(HammerOnPullOffSegment* item, LayoutContext& ctx); + static void layoutHammerOnPullOffText(HammerOnPullOffText* item, LayoutContext& ctx); static void fillHairpinSegmentShape(const HairpinSegment* item, HairpinSegment::LayoutData* ldata); static void layoutHarpPedalDiagram(const HarpPedalDiagram* item, HarpPedalDiagram::LayoutData* ldata); static void layoutHarmonicMarkSegment(HarmonicMarkSegment* item, LayoutContext& ctx); diff --git a/src/engraving/rendering/single/singledraw.cpp b/src/engraving/rendering/single/singledraw.cpp index fc80384cd9dab..727f11d6e13a2 100644 --- a/src/engraving/rendering/single/singledraw.cpp +++ b/src/engraving/rendering/single/singledraw.cpp @@ -60,6 +60,7 @@ #include "dom/guitarbend.h" #include "dom/hairpin.h" +#include "dom/hammeronpulloff.h" #include "dom/harppedaldiagram.h" #include "dom/harmonicmark.h" #include "dom/harmony.h" @@ -209,6 +210,10 @@ void SingleDraw::drawItem(const EngravingItem* item, Painter* painter) case ElementType::HAIRPIN_SEGMENT: draw(item_cast(item), painter); break; + case ElementType::HAMMER_ON_PULL_OFF_SEGMENT: draw(item_cast(item), painter); + break; + case ElementType::HAMMER_ON_PULL_OFF_TEXT: draw(item_cast(item), painter); + break; case ElementType::HARP_DIAGRAM: draw(item_cast(item), painter); break; case ElementType::HARMONIC_MARK_SEGMENT: draw(item_cast(item), painter); @@ -1764,6 +1769,16 @@ void SingleDraw::draw(const HairpinSegment* item, Painter* painter) } } +void SingleDraw::draw(const HammerOnPullOffSegment* item, muse::draw::Painter* painter) +{ + draw(toSlurSegment(item), painter); +} + +void SingleDraw::draw(const HammerOnPullOffText *item, Painter *painter) +{ + drawTextBase(item, painter); +} + void SingleDraw::draw(const HarpPedalDiagram* item, Painter* painter) { TRACE_DRAW_ITEM; diff --git a/src/engraving/rendering/single/singledraw.h b/src/engraving/rendering/single/singledraw.h index ccfce45fec280..b144c7c16002a 100644 --- a/src/engraving/rendering/single/singledraw.h +++ b/src/engraving/rendering/single/singledraw.h @@ -71,6 +71,8 @@ class GuitarBendSegment; class Hairpin; class HairpinSegment; +class HammerOnPullOffSegment; +class HammerOnPullOffText; class HarpPedalDiagram; class HarmonicMarkSegment; class Harmony; @@ -200,6 +202,8 @@ class SingleDraw static void draw(const GuitarBendSegment* item, muse::draw::Painter* painter); static void draw(const HairpinSegment* item, muse::draw::Painter* painter); + static void draw(const HammerOnPullOffSegment* item, muse::draw::Painter* painter); + static void draw(const HammerOnPullOffText* item, muse::draw::Painter* painter); static void draw(const HarpPedalDiagram* item, muse::draw::Painter* painter); static void draw(const HarmonicMarkSegment* item, muse::draw::Painter* painter); static void draw(const Harmony* item, muse::draw::Painter* painter); diff --git a/src/engraving/rendering/single/singlelayout.cpp b/src/engraving/rendering/single/singlelayout.cpp index 33cac59c7b4fd..268d40db557b5 100644 --- a/src/engraving/rendering/single/singlelayout.cpp +++ b/src/engraving/rendering/single/singlelayout.cpp @@ -53,6 +53,7 @@ #include "dom/gradualtempochange.h" #include "dom/guitarbend.h" #include "dom/hairpin.h" +#include "dom/hammeronpulloff.h" #include "dom/harppedaldiagram.h" #include "dom/instrchange.h" #include "dom/jump.h" @@ -157,6 +158,8 @@ void SingleLayout::layoutItem(EngravingItem* item) break; case ElementType::HAIRPIN: layout(toHairpin(item), ctx); break; + case ElementType::HAMMER_ON_PULL_OFF: layout(toHammerOnPullOff(item), ctx); + break; case ElementType::HARP_DIAGRAM: layout(toHarpPedalDiagram(item), ctx); break; case ElementType::IMAGE: layout(toImage(item), ctx); @@ -994,6 +997,59 @@ void SingleLayout::layout(Hairpin* item, const Context& ctx) layoutLine(item, ctx); } +void SingleLayout::layout(HammerOnPullOff* item, const Context& ctx) +{ + double spatium = item->spatium(); + HammerOnPullOffSegment* s = nullptr; + if (item->spannerSegments().empty()) { + s = new HammerOnPullOffSegment(ctx.dummyParent()->system()); + s->setTrack(item->track()); + item->add(s); + } else { + s = toHammerOnPullOffSegment(item->frontSegment()); + } + + s->setSpannerSegmentType(SpannerSegmentType::SINGLE); + + s->setPos(PointF()); + s->ups(Grip::START).p = PointF(0, 0); + s->ups(Grip::END).p = PointF(spatium * 6, 0); + s->setExtraHeight(0.0); + + SlurTieLayout::computeBezier(s); + + layout(s, ctx); + + item->setbbox(s->ldata()->bbox()); +} + +void SingleLayout::layout(HammerOnPullOffSegment* item, const Context& ctx) +{ + const std::vector& hopoTexts = item->hopoText(); + if (item->hopoText().empty()) { + HammerOnPullOffText* hopoText = new HammerOnPullOffText(item); + hopoText->setParent(item); + hopoText->setXmlText("H/P"); + item->addHopoText(hopoText); + } + + HammerOnPullOffText* hopoText = hopoTexts.front(); + Align align; + align.vertical = AlignV::BASELINE; + align.horizontal = AlignH::HCENTER; + hopoText->setAlign(align); + layoutTextBase(hopoText, ctx, hopoText->mutldata()); + + RectF bbox = item->ldata()->bbox(); + double x = 0.5 * (bbox.left() + bbox.right()); + double y = bbox.top() - 0.5 * item->spatium(); + hopoText->mutldata()->setPos(x, y); + + Shape itemShape = item->mutldata()->shape(); + itemShape.add(hopoText->shape().translated(hopoText->pos())); + item->mutldata()->setShape(itemShape); +} + void SingleLayout::layout(HairpinSegment* item, const Context& ctx) { const double spatium = item->spatium(); diff --git a/src/engraving/rendering/single/singlelayout.h b/src/engraving/rendering/single/singlelayout.h index f76f14a56db77..9c2d6d089f08a 100644 --- a/src/engraving/rendering/single/singlelayout.h +++ b/src/engraving/rendering/single/singlelayout.h @@ -186,6 +186,8 @@ class SingleLayout static void layout(GuitarBend* item, const Context& ctx); static void layout(Hairpin* item, const Context& ctx); + static void layout(HammerOnPullOff* item, const Context& ctx); + static void layout(HammerOnPullOffSegment* item, const Context& ctx); static void layout(HarpPedalDiagram* item, const Context& ctx); static void layout(Image* item, const Context& ctx); diff --git a/src/engraving/rw/read460/connectorinforeader.cpp b/src/engraving/rw/read460/connectorinforeader.cpp index 3bad0838dce85..df5ec440a8258 100644 --- a/src/engraving/rw/read460/connectorinforeader.cpp +++ b/src/engraving/rw/read460/connectorinforeader.cpp @@ -256,6 +256,7 @@ void ConnectorInfoReader::readAddConnector(ChordRest* item, ConnectorInfoReader* const ElementType type = info->type(); switch (type) { case ElementType::SLUR: + case ElementType::HAMMER_ON_PULL_OFF: { Spanner* spanner = toSpanner(info->connector()); const Location& l = info->location(); diff --git a/src/engraving/rw/read460/tread.cpp b/src/engraving/rw/read460/tread.cpp index f6005f1d219c7..0d6b3c5f6a799 100644 --- a/src/engraving/rw/read460/tread.cpp +++ b/src/engraving/rw/read460/tread.cpp @@ -111,6 +111,7 @@ #include "../../dom/groups.h" #include "../../dom/harppedaldiagram.h" #include "../../dom/hairpin.h" +#include "../../dom/hammeronpulloff.h" #include "../../dom/keysig.h" #include "../../dom/layoutbreak.h" #include "../../dom/ledgerline.h" @@ -216,6 +217,8 @@ void TRead::readItem(EngravingItem* item, XmlReader& xml, ReadContext& ctx) break; case ElementType::HAIRPIN: read(item_cast(item), xml, ctx); break; + case ElementType::HAMMER_ON_PULL_OFF: read(item_cast(item), xml, ctx); + break; case ElementType::HARMONY: read(item_cast(item), xml, ctx); break; case ElementType::HARMONIC_MARK: read(item_cast(item), xml, ctx); @@ -3134,6 +3137,15 @@ void TRead::read(Hairpin* h, XmlReader& e, ReadContext& ctx) h->styleChanged(); } +void TRead::read(HammerOnPullOff* h, XmlReader& xml, ReadContext& ctx) +{ + while (xml.readNextStartElement()) { + if (!readProperties(static_cast(h), xml, ctx)) { + xml.unknown(); + } + } +} + void TRead::read(Harmony* h, XmlReader& e, ReadContext& ctx) { while (e.readNextStartElement()) { @@ -3996,7 +4008,8 @@ bool TRead::readProperties(SlurTie* s, XmlReader& e, ReadContext& ctx) if (TRead::readProperty(s, tag, e, ctx, Pid::SLUR_DIRECTION)) { } else if (tag == "lineType") { s->setStyleType(static_cast(e.readInt())); - } else if (tag == "SlurSegment" || tag == "TieSegment" || tag == "LaissezVibSegment" || tag == "PartialTieSegment") { + } else if (tag == "SlurSegment" || tag == "TieSegment" || tag == "LaissezVibSegment" || tag == "PartialTieSegment" + || tag == "HammerOnPullOffSegment") { const int idx = e.intAttribute("no", 0); const int n = int(s->spannerSegments().size()); for (int i = n; i < idx; ++i) { @@ -4026,12 +4039,32 @@ void TRead::read(SlurTieSegment* s, XmlReader& e, ReadContext& ctx) s->ups(Grip::BEZIER2).off = e.readPoint() * _spatium; } else if (tag == "o4") { s->ups(Grip::END).off = e.readPoint() * _spatium; + } else if (tag == "HammerOnPullOffText") { + DO_ASSERT(s->isHammerOnPullOffSegment()); + readHopoText(toHammerOnPullOffSegment(s), e, ctx, e.intAttribute("idx")); } else if (!readItemProperties(s, e, ctx)) { e.unknown(); } } } +void TRead::readHopoText(HammerOnPullOffSegment* hopoSeg, XmlReader& xml, ReadContext& ctx, int idx) +{ + int hopoTextCount = static_cast(hopoSeg->hopoText().size()); + for (int i = hopoTextCount; i < idx; ++i) { + hopoSeg->addHopoText(new HammerOnPullOffText(hopoSeg)); + } + + HammerOnPullOffText* hopoText = new HammerOnPullOffText(hopoSeg); + while (xml.readNextStartElement()) { + if (!readProperties(toTextBase(hopoText), xml, ctx)) { + xml.unknown(); + } + } + + hopoSeg->addHopoText(hopoText); +} + bool TRead::readProperties(Spanner* s, XmlReader& e, ReadContext& ctx) { const AsciiStringView tag(e.name()); diff --git a/src/engraving/rw/read460/tread.h b/src/engraving/rw/read460/tread.h index 5c3a106b9e3fd..cea793fe45fc7 100644 --- a/src/engraving/rw/read460/tread.h +++ b/src/engraving/rw/read460/tread.h @@ -81,6 +81,7 @@ class GuitarBendSegment; class GuitarBendHold; class Hairpin; +class HammerOnPullOff; class Harmony; class HarmonicMark; class HarpPedalDiagram; @@ -232,6 +233,7 @@ class TRead static void read(GuitarBendHold* h, XmlReader& xml, ReadContext& ctx); static void read(Hairpin* h, XmlReader& xml, ReadContext& ctx); + static void read(HammerOnPullOff* h, XmlReader& xml, ReadContext& ctx); static void read(Harmony* h, XmlReader& xml, ReadContext& ctx); static void read(HarpPedalDiagram* h, XmlReader& xml, ReadContext& ctx); static void read(HarmonicMark* h, XmlReader& xml, ReadContext& ctx); @@ -387,5 +389,7 @@ class TRead static bool readProperties(StaffTextBase* t, XmlReader& xml, ReadContext& ctx); static void readSystemLock(Score* score, XmlReader& e); + + static void readHopoText(HammerOnPullOffSegment* hopoSeg, XmlReader& xml, ReadContext& ctx, int idx); }; } diff --git a/src/engraving/rw/write/twrite.cpp b/src/engraving/rw/write/twrite.cpp index e1dc6fe83492c..1dab33fb2d12a 100644 --- a/src/engraving/rw/write/twrite.cpp +++ b/src/engraving/rw/write/twrite.cpp @@ -75,6 +75,7 @@ #include "dom/guitarbend.h" #include "dom/hairpin.h" +#include "dom/hammeronpulloff.h" #include "dom/harmony.h" #include "dom/harmonicmark.h" #include "dom/harppedaldiagram.h" @@ -232,6 +233,8 @@ void TWrite::writeItem(const EngravingItem* item, XmlWriter& xml, WriteContext& break; case ElementType::HAIRPIN: write(item_cast(item), xml, ctx); break; + case ElementType::HAMMER_ON_PULL_OFF: write(item_cast(item), xml, ctx); + break; case ElementType::HARMONY: write(item_cast(item), xml, ctx); break; case ElementType::HARMONIC_MARK: write(item_cast(item), xml, ctx); @@ -1643,6 +1646,47 @@ void TWrite::write(const Hairpin* item, XmlWriter& xml, WriteContext& ctx) xml.endElement(); } +void TWrite::write(const HammerOnPullOff* item, XmlWriter& xml, WriteContext& ctx) +{ + if (item->broken()) { + return; + } + if (!ctx.canWrite(item)) { + return; + } + + xml.startElement(item); + if (ctx.clipboardmode()) { + xml.tag("stemArr", Slur::calcStemArrangement(item->startElement(), item->endElement())); + } + + writeProperty(item, xml, Pid::PARTIAL_SPANNER_DIRECTION); + + writeProperties(static_cast(item), xml, ctx); + + xml.endElement(); +} + +void TWrite::writeProperties(const HammerOnPullOffSegment* seg, XmlWriter& xml, WriteContext& ctx) +{ + for (size_t i = 0; i < seg->hopoText().size(); ++i) { + HammerOnPullOffText* hopoText = seg->hopoText()[i]; + if (!hopoText->isUserModified()) { + continue; + } + write(hopoText, xml, ctx, i); + } +} + +void TWrite::write(const HammerOnPullOffText* item, XmlWriter& xml, WriteContext& ctx, size_t idx) +{ + xml.startElement(item, { { "idx", idx } }); + + writeProperties(toTextBase(item), xml, ctx, /*writeText*/ false); + + xml.endElement(); +} + void TWrite::write(const Harmony* item, XmlWriter& xml, WriteContext& ctx) { if (!ctx.canWrite(item)) { @@ -2593,14 +2637,7 @@ void TWrite::writeProperties(const SlurTie* item, XmlWriter& xml, WriteContext& void TWrite::writeSlur(const SlurTieSegment* seg, XmlWriter& xml, WriteContext& ctx, int no) { - if (seg->visible() && seg->autoplace() - && (seg->color() == ctx.configuration()->defaultColor()) - && seg->offset().isNull() - && seg->ups(Grip::START).off.isNull() - && seg->ups(Grip::BEZIER1).off.isNull() - && seg->ups(Grip::BEZIER2).off.isNull() - && seg->ups(Grip::END).off.isNull() - ) { + if (!seg->isUserModified()) { return; } @@ -2619,6 +2656,11 @@ void TWrite::writeSlur(const SlurTieSegment* seg, XmlWriter& xml, WriteContext& if (!seg->ups(Grip::END).off.isNull()) { xml.tagPoint("o4", seg->ups(Grip::END).off / _spatium); } + + if (seg->isHammerOnPullOffSegment()) { + writeProperties(toHammerOnPullOffSegment(seg), xml, ctx); + } + writeItemProperties(seg, xml, ctx); xml.endElement(); } diff --git a/src/engraving/rw/write/twrite.h b/src/engraving/rw/write/twrite.h index 8faef88df7e51..ea705743c3545 100644 --- a/src/engraving/rw/write/twrite.h +++ b/src/engraving/rw/write/twrite.h @@ -75,6 +75,9 @@ class GuitarBend; class GuitarBendSegment; class Hairpin; +class HammerOnPullOff; +class HammerOnPullOffSegment; +class HammerOnPullOffText; class Harmony; class HarmonicMark; class HarpPedalDiagram; @@ -217,6 +220,7 @@ class TWrite static void write(const GuitarBend* item, XmlWriter& xml, WriteContext& ctx); static void write(const Hairpin* item, XmlWriter& xml, WriteContext& ctx); + static void write(const HammerOnPullOff* item, XmlWriter& xml, WriteContext& ctx); static void write(const Harmony* item, XmlWriter& xml, WriteContext& ctx); static void write(const HarmonicMark* item, XmlWriter& xml, WriteContext& ctx); static void write(const HarpPedalDiagram* item, XmlWriter& xml, WriteContext& ctx); @@ -334,6 +338,10 @@ class TWrite static void writeProperties(const StaffTextBase* item, XmlWriter& xml, WriteContext& ctx); static void writeProperties(const SlurTie* item, XmlWriter& xml, WriteContext& ctx); static void writeSlur(const SlurTieSegment* seg, XmlWriter& xml, WriteContext& ctx, int no); + + static void writeProperties(const HammerOnPullOffSegment* seg, XmlWriter& xml, WriteContext& ctx); + static void write(const HammerOnPullOffText* item, XmlWriter& xml, WriteContext& ctx, size_t idx); + static void writeProperties(const SLine* item, XmlWriter& xml, WriteContext& ctx); static void writeProperties(const GuitarBendSegment* item, XmlWriter& xml, WriteContext& ctx); static void writeProperties(const Spanner* item, XmlWriter& xml, WriteContext& ctx); diff --git a/src/engraving/style/styledef.cpp b/src/engraving/style/styledef.cpp index 0cf0980d67a17..b72adee405656 100644 --- a/src/engraving/style/styledef.cpp +++ b/src/engraving/style/styledef.cpp @@ -897,6 +897,26 @@ const std::array StyleDef::styleValue styleDef(rhGuitarFingeringFrameBgColor, PropertyValue::fromValue(Color::transparent)), styleDef(rhGuitarFingeringOffset, PointF()), + styleDef(hammerOnPullOffTappingFontFace, "Edwin"), + styleDef(hammerOnPullOffTappingFontSize, 10.0), + styleDef(hammerOnPullOffTappingLineSpacing, 1.0), + styleDef(hammerOnPullOffTappingFontSpatiumDependent, true), + styleDef(hammerOnPullOffTappingFontStyle, int(FontStyle::Normal)), + styleDef(hammerOnPullOffTappingColor, PropertyValue::fromValue(Color::BLACK)), + styleDef(hammerOnPullOffTappingAlign, Align(AlignH::LEFT, AlignV::BASELINE)), + styleDef(hammerOnPullOffTappingFrameType, int(FrameType::NO_FRAME)), + styleDef(hammerOnPullOffTappingFramePadding, 0.2), + styleDef(hammerOnPullOffTappingFrameWidth, 0.1), + styleDef(hammerOnPullOffTappingFrameRound, 0), + styleDef(hammerOnPullOffTappingFrameFgColor, PropertyValue::fromValue(Color::BLACK)), + styleDef(hammerOnPullOffTappingFrameBgColor, PropertyValue::fromValue(Color::transparent)), + styleDef(hammerOnPullOffTappingOffset, PointF()), + + styleDef(hopoShowOnStandardStaves, true), + styleDef(hopoShowOnTabStaves, true), + styleDef(hopoUpperCase, true), + styleDef(hopoShowAll, true), + styleDef(stringNumberFontFace, "Edwin"), styleDef(stringNumberFontSize, 8.0), styleDef(stringNumberLineSpacing, 1.0), diff --git a/src/engraving/style/styledef.h b/src/engraving/style/styledef.h index 8e407d520a0b3..76e6d1ddb444d 100644 --- a/src/engraving/style/styledef.h +++ b/src/engraving/style/styledef.h @@ -909,6 +909,26 @@ enum class Sid { rhGuitarFingeringFrameBgColor, rhGuitarFingeringOffset, + hammerOnPullOffTappingFontFace, + hammerOnPullOffTappingFontSize, + hammerOnPullOffTappingLineSpacing, + hammerOnPullOffTappingFontSpatiumDependent, + hammerOnPullOffTappingFontStyle, + hammerOnPullOffTappingColor, + hammerOnPullOffTappingAlign, + hammerOnPullOffTappingFrameType, + hammerOnPullOffTappingFramePadding, + hammerOnPullOffTappingFrameWidth, + hammerOnPullOffTappingFrameRound, + hammerOnPullOffTappingFrameFgColor, + hammerOnPullOffTappingFrameBgColor, + hammerOnPullOffTappingOffset, + + hopoShowOnStandardStaves, + hopoShowOnTabStaves, + hopoUpperCase, + hopoShowAll, + stringNumberFontFace, stringNumberFontSize, stringNumberLineSpacing, diff --git a/src/engraving/style/textstyle.cpp b/src/engraving/style/textstyle.cpp index 0d35f67414eb9..8cbbc17d1ca0e 100644 --- a/src/engraving/style/textstyle.cpp +++ b/src/engraving/style/textstyle.cpp @@ -207,6 +207,24 @@ const TextStyle rhGuitarFingeringTextStyle { { { TextStylePropertyType::MusicalSymbolsScale, Sid::dummyMusicalSymbolsScale, Pid::MUSICAL_SYMBOLS_SCALE }, } }; +const TextStyle hammerOnPullOffTextStyle { { + { TextStylePropertyType::FontFace, Sid::hammerOnPullOffTappingFontFace, Pid::FONT_FACE }, + { TextStylePropertyType::FontSize, Sid::hammerOnPullOffTappingFontSize, Pid::FONT_SIZE }, + { TextStylePropertyType::LineSpacing, Sid::hammerOnPullOffTappingLineSpacing, Pid::TEXT_LINE_SPACING }, + { TextStylePropertyType::SizeSpatiumDependent, Sid::hammerOnPullOffTappingFontSpatiumDependent, Pid::SIZE_SPATIUM_DEPENDENT }, + { TextStylePropertyType::FontStyle, Sid::hammerOnPullOffTappingFontStyle, Pid::FONT_STYLE }, + { TextStylePropertyType::Color, Sid::hammerOnPullOffTappingColor, Pid::COLOR }, + { TextStylePropertyType::TextAlign, Sid::hammerOnPullOffTappingAlign, Pid::ALIGN }, + { TextStylePropertyType::Offset, Sid::hammerOnPullOffTappingOffset, Pid::OFFSET }, + { TextStylePropertyType::FrameType, Sid::hammerOnPullOffTappingFrameType, Pid::FRAME_TYPE }, + { TextStylePropertyType::FramePadding, Sid::hammerOnPullOffTappingFramePadding, Pid::FRAME_PADDING }, + { TextStylePropertyType::FrameWidth, Sid::hammerOnPullOffTappingFrameWidth, Pid::FRAME_WIDTH }, + { TextStylePropertyType::FrameRound, Sid::hammerOnPullOffTappingFrameRound, Pid::FRAME_ROUND }, + { TextStylePropertyType::FrameBorderColor, Sid::hammerOnPullOffTappingFrameFgColor, Pid::FRAME_FG_COLOR }, + { TextStylePropertyType::FrameFillColor, Sid::hammerOnPullOffTappingFrameBgColor, Pid::FRAME_BG_COLOR }, + { TextStylePropertyType::MusicalSymbolsScale, Sid::dummyMusicalSymbolsScale, Pid::MUSICAL_SYMBOLS_SCALE }, +} }; + const TextStyle stringNumberTextStyle { { { TextStylePropertyType::FontFace, Sid::stringNumberFontFace, Pid::FONT_FACE }, { TextStylePropertyType::FontSize, Sid::stringNumberFontSize, Pid::FONT_SIZE }, @@ -1262,6 +1280,7 @@ const TextStyle* textStyle(TextStyleType idx) case TextStyleType::FINGERING: return &fingeringTextStyle; case TextStyleType::LH_GUITAR_FINGERING: return &lhGuitarFingeringTextStyle; case TextStyleType::RH_GUITAR_FINGERING: return &rhGuitarFingeringTextStyle; + case TextStyleType::HAMMER_ON_PULL_OFF: return &hammerOnPullOffTextStyle; case TextStyleType::STRING_NUMBER: return &stringNumberTextStyle; case TextStyleType::STRING_TUNINGS: return &stringTuningsStyle; // todo case TextStyleType::FRET_DIAGRAM_FINGERING: return &fretDiagramFingeringStyle; diff --git a/src/engraving/types/types.h b/src/engraving/types/types.h index 7b9c05bd1e311..7b15a547d1c25 100644 --- a/src/engraving/types/types.h +++ b/src/engraving/types/types.h @@ -205,6 +205,9 @@ enum class ElementType : unsigned char { TREMOLO_SINGLECHORD, TIME_TICK_ANCHOR, PARENTHESIS, + HAMMER_ON_PULL_OFF, + HAMMER_ON_PULL_OFF_SEGMENT, + HAMMER_ON_PULL_OFF_TEXT, ROOT_ITEM, DUMMY, @@ -802,6 +805,7 @@ enum class TextStyleType : unsigned char { FINGERING, LH_GUITAR_FINGERING, RH_GUITAR_FINGERING, + HAMMER_ON_PULL_OFF, STRING_NUMBER, STRING_TUNINGS, FRET_DIAGRAM_FINGERING, diff --git a/src/engraving/types/typesconv.cpp b/src/engraving/types/typesconv.cpp index d84a8350c2922..97e90d7f8a425 100644 --- a/src/engraving/types/typesconv.cpp +++ b/src/engraving/types/typesconv.cpp @@ -289,6 +289,7 @@ static const std::vector > ELEMENT_TYPES = { { ElementType::SYSTEM, "System", muse::TranslatableString("engraving", "System") }, { ElementType::CHORD, "Chord", muse::TranslatableString("engraving", "Chord") }, { ElementType::SLUR, "Slur", muse::TranslatableString("engraving", "Slur") }, + { ElementType::HAMMER_ON_PULL_OFF, "HammerOnPullOff", muse::TranslatableString("engraving", "Hammer-on pull-off") }, { ElementType::HBOX, "HBox", muse::TranslatableString("engraving", "Horizontal frame") }, { ElementType::VBOX, "VBox", muse::TranslatableString("engraving", "Vertical frame") }, { ElementType::TBOX, "TBox", muse::TranslatableString("engraving", "Text frame") }, @@ -307,6 +308,10 @@ static const std::vector > ELEMENT_TYPES = { { ElementType::TREMOLO_SINGLECHORD, "TremoloSingleChord", muse::TranslatableString("engraving", "Tremolo") }, { ElementType::TREMOLO_TWOCHORD, "TremoloTwoChord", muse::TranslatableString("engraving", "Tremolo") }, { ElementType::TIME_TICK_ANCHOR, "TimeTickAnchor", muse::TranslatableString("engraving", "Time tick anchor") }, + { ElementType::HAMMER_ON_PULL_OFF, "HammerOnPullOff", muse::TranslatableString("engraving", "Hammer-on / pull-off") }, + { ElementType::HAMMER_ON_PULL_OFF_SEGMENT, "HammerOnPullOffSegment", + muse::TranslatableString("engraving", "Hammer-on / pull-off segment") }, + { ElementType::HAMMER_ON_PULL_OFF_TEXT, "HammerOnPullOffText", muse::TranslatableString("engraving", "Hammer-on / pull-off text") }, { ElementType::ROOT_ITEM, "RootItem", muse::TranslatableString::untranslatable("Root item") }, { ElementType::DUMMY, "Dummy", muse::TranslatableString::untranslatable("Dummy") }, }; @@ -1255,6 +1260,8 @@ static const std::vector > TEXTSTYLE_TYPES = { { TextStyleType::FINGERING, "fingering", muse::TranslatableString("engraving", "Fingering") }, { TextStyleType::LH_GUITAR_FINGERING, "guitar_fingering_lh", muse::TranslatableString("engraving", "LH guitar fingering") }, { TextStyleType::RH_GUITAR_FINGERING, "guitar_fingering_rh", muse::TranslatableString("engraving", "RH guitar fingering") }, + { TextStyleType::HAMMER_ON_PULL_OFF, "hammer_on_pull_off", + muse::TranslatableString("engraving", "Hammer-ons, pull-offs, and tapping") }, { TextStyleType::STRING_NUMBER, "string_number", muse::TranslatableString("engraving", "String number") }, { TextStyleType::STRING_TUNINGS, "string_tunings", muse::TranslatableString("engraving", "String tunings") }, { TextStyleType::FRET_DIAGRAM_FINGERING, "fret_diagram_fingering", diff --git a/src/inspector/internal/services/elementrepositoryservice.cpp b/src/inspector/internal/services/elementrepositoryservice.cpp index 034e2c1329916..2faacf1f6590b 100644 --- a/src/inspector/internal/services/elementrepositoryservice.cpp +++ b/src/inspector/internal/services/elementrepositoryservice.cpp @@ -104,6 +104,7 @@ QList ElementRepositoryService::findElementsByTyp case mu::engraving::ElementType::TEXTLINE: case mu::engraving::ElementType::NOTELINE: case mu::engraving::ElementType::SLUR: + case mu::engraving::ElementType::HAMMER_ON_PULL_OFF: case mu::engraving::ElementType::TIE: case mu::engraving::ElementType::LAISSEZ_VIB: case mu::engraving::ElementType::PARTIAL_TIE: @@ -323,6 +324,7 @@ QList ElementRepositoryService::findLines(mu::eng { mu::engraving::ElementType::TEXTLINE, mu::engraving::ElementType::TEXTLINE_SEGMENT }, { mu::engraving::ElementType::NOTELINE, mu::engraving::ElementType::NOTELINE_SEGMENT }, { mu::engraving::ElementType::SLUR, mu::engraving::ElementType::SLUR_SEGMENT }, + { mu::engraving::ElementType::HAMMER_ON_PULL_OFF, mu::engraving::ElementType::HAMMER_ON_PULL_OFF_SEGMENT }, { mu::engraving::ElementType::TIE, mu::engraving::ElementType::TIE_SEGMENT }, { mu::engraving::ElementType::LAISSEZ_VIB, mu::engraving::ElementType::LAISSEZ_VIB_SEGMENT }, { mu::engraving::ElementType::PARTIAL_TIE, mu::engraving::ElementType::PARTIAL_TIE_SEGMENT }, diff --git a/src/inspector/types/texttypes.h b/src/inspector/types/texttypes.h index 358d2358dece5..536e35250e9d8 100644 --- a/src/inspector/types/texttypes.h +++ b/src/inspector/types/texttypes.h @@ -164,7 +164,8 @@ static const QList TEXT_ELEMENT_TYPES = { mu::engraving::ElementType::CAPO, mu::engraving::ElementType::STRING_TUNINGS, mu::engraving::ElementType::HARP_DIAGRAM, - mu::engraving::ElementType::SOUND_FLAG + mu::engraving::ElementType::SOUND_FLAG, + mu::engraving::ElementType::HAMMER_ON_PULL_OFF_TEXT }; } diff --git a/src/notation/inotationinteraction.h b/src/notation/inotationinteraction.h index e6e3a2c4dab70..4f9e1fa7edfc6 100644 --- a/src/notation/inotationinteraction.h +++ b/src/notation/inotationinteraction.h @@ -192,6 +192,7 @@ class INotationInteraction virtual void addTiedNoteToChord() = 0; virtual void addLaissezVibToSelection() = 0; virtual void addSlurToSelection() = 0; + virtual void addHammerOnPullOffToSelection() = 0; virtual void addOttavaToSelection(OttavaType type) = 0; virtual void addHairpinOnGripDrag(engraving::EditData& ed, bool isLeftGrip) = 0; virtual void addHairpinsToSelection(HairpinType type) = 0; diff --git a/src/notation/internal/notationactioncontroller.cpp b/src/notation/internal/notationactioncontroller.cpp index 5b9ec0a065679..e47f7d171bae7 100644 --- a/src/notation/internal/notationactioncontroller.cpp +++ b/src/notation/internal/notationactioncontroller.cpp @@ -229,6 +229,7 @@ void NotationActionController::init() registerAction("chord-tie", &Controller::chordTie); registerAction("lv", &Controller::addLaissezVib); registerAction("add-slur", &Controller::addSlur); + registerAction("hammer-on-pull-off", &Controller::addHammerOnPullOff); registerAction(UNDO_ACTION_CODE, &Interaction::undo, &Controller::canUndo); registerAction(REDO_ACTION_CODE, &Interaction::redo, &Controller::canRedo); @@ -1390,6 +1391,16 @@ void NotationActionController::addSlur() } } +void NotationActionController::addHammerOnPullOff() +{ + auto interaction = currentNotationInteraction(); + if (!interaction) { + return; + } + + interaction->addHammerOnPullOffToSelection(); +} + void NotationActionController::addFret(int num) { auto interaction = currentNotationInteraction(); diff --git a/src/notation/internal/notationactioncontroller.h b/src/notation/internal/notationactioncontroller.h index 6b2308cd5248d..3e175a9076028 100644 --- a/src/notation/internal/notationactioncontroller.h +++ b/src/notation/internal/notationactioncontroller.h @@ -115,6 +115,7 @@ class NotationActionController : public muse::actions::Actionable, public muse:: void chordTie(); void addLaissezVib(); void addSlur(); + void addHammerOnPullOff(); void addFret(int num); void insertClef(mu::engraving::ClefType type); diff --git a/src/notation/internal/notationinteraction.cpp b/src/notation/internal/notationinteraction.cpp index dcc1fd3e7fd55..939349f32e094 100644 --- a/src/notation/internal/notationinteraction.cpp +++ b/src/notation/internal/notationinteraction.cpp @@ -59,6 +59,7 @@ #include "engraving/dom/factory.h" #include "engraving/dom/figuredbass.h" #include "engraving/dom/guitarbend.h" +#include "engraving/dom/hammeronpulloff.h" #include "engraving/dom/image.h" #include "engraving/dom/instrchange.h" #include "engraving/dom/gradualtempochange.h" @@ -1541,6 +1542,7 @@ bool NotationInteraction::updateDropSingle(const PointF& pos, Qt::KeyboardModifi case ElementType::REHEARSAL_MARK: case ElementType::CHORD: case ElementType::SLUR: + case ElementType::HAMMER_ON_PULL_OFF: case ElementType::HARMONY: case ElementType::BAGPIPE_EMBELLISHMENT: case ElementType::AMBITUS: @@ -1857,6 +1859,7 @@ bool NotationInteraction::dropSingle(const PointF& pos, Qt::KeyboardModifiers mo } break; case ElementType::SLUR: + case ElementType::HAMMER_ON_PULL_OFF: { EngravingItem* el = dropTarget(edd.ed); mu::engraving::Slur* dropElement = toSlur(edd.ed.dropElement); @@ -5058,6 +5061,17 @@ void NotationInteraction::addSlurToSelection() doAddSlur(); } +void NotationInteraction::addHammerOnPullOffToSelection() +{ + if (selection()->isNone()) { + return; + } + + HammerOnPullOff* hopo = Factory::createHammerOnPullOff(score()->dummy()); + // Calls `startEdit` internally + doAddSlur(hopo); +} + void NotationInteraction::addOttavaToSelection(OttavaType type) { if (selection()->isNone()) { diff --git a/src/notation/internal/notationinteraction.h b/src/notation/internal/notationinteraction.h index b39ed325117d9..023a6c67b5240 100644 --- a/src/notation/internal/notationinteraction.h +++ b/src/notation/internal/notationinteraction.h @@ -199,6 +199,7 @@ class NotationInteraction : public INotationInteraction, public muse::Injectable void addLaissezVibToSelection() override; void addTiedNoteToChord() override; void addSlurToSelection() override; + void addHammerOnPullOffToSelection() override; void addOttavaToSelection(OttavaType type) override; void addHairpinOnGripDrag(engraving::EditData& ed, bool isLeftGrip) override; void addHairpinsToSelection(HairpinType type) override; diff --git a/src/notation/internal/notationuiactions.cpp b/src/notation/internal/notationuiactions.cpp index 093ef72d53b35..cb4e3887a3b2d 100644 --- a/src/notation/internal/notationuiactions.cpp +++ b/src/notation/internal/notationuiactions.cpp @@ -2525,6 +2525,12 @@ const UiActionList NotationUiActions::m_actions = { TranslatableString("action", "Slight bend"), IconCode::Code::GUITAR_SLIGHT_BEND ), + UiAction("hammer-on-pull-off", + mu::context::UiCtxProjectFocused, + mu::context::CTX_NOTATION_OPENED, + TranslatableString("action", "Hammer-on/pull-off"), + TranslatableString("action", "Add hammer-on/pull-off") + ) }; const UiActionList NotationUiActions::m_scoreConfigActions = { diff --git a/src/notation/notationmodule.cpp b/src/notation/notationmodule.cpp index 2e1937e2298b4..31d94c874bf2f 100644 --- a/src/notation/notationmodule.cpp +++ b/src/notation/notationmodule.cpp @@ -98,6 +98,7 @@ #include "view/styledialog/glissandosectionmodel.h" #include "view/styledialog/notelinesectionmodel.h" #include "view/styledialog/clefkeytimesigpagemodel.h" +#include "view/styledialog/hammeronpullofftappingpagemodel.h" #include "diagnostics/idiagnosticspathsregister.h" @@ -230,6 +231,7 @@ void NotationModule::registerUiTypes() qmlRegisterType("MuseScore.NotationScene", 1, 0, "GlissandoSectionModel"); qmlRegisterType("MuseScore.NotationScene", 1, 0, "NoteLineSectionModel"); qmlRegisterType("MuseScore.NotationScene", 1, 0, "ClefKeyTimeSigPageModel"); + qmlRegisterType("MuseScore.NotationScene", 1, 0, "HammerOnPullOffTappingPageModel"); qmlRegisterUncreatableType("MuseScore.NotationScene", 1, 0, "NoteInputBarCustomiseItem", "Cannot create"); diff --git a/src/notation/notationscene.qrc b/src/notation/notationscene.qrc index e9f7ef1043477..b3ac58ec65c28 100644 --- a/src/notation/notationscene.qrc +++ b/src/notation/notationscene.qrc @@ -99,5 +99,8 @@ qml/MuseScore/NotationScene/internal/EditStyle/courtesyImages/repeats-on-parens.png qml/MuseScore/NotationScene/internal/EditStyle/courtesyImages/repeats-on.png qml/MuseScore/NotationScene/internal/SelectionFilterSection.qml + qml/MuseScore/NotationScene/internal/EditStyle/HammerOnPullOffTappingPage.qml + qml/MuseScore/NotationScene/internal/EditStyle/hammerOnPullOffImages/hopoShowAll.png + qml/MuseScore/NotationScene/internal/EditStyle/hammerOnPullOffImages/hopoNotShowAll.png diff --git a/src/notation/qml/MuseScore/NotationScene/internal/EditStyle/HammerOnPullOffTappingPage.qml b/src/notation/qml/MuseScore/NotationScene/internal/EditStyle/HammerOnPullOffTappingPage.qml new file mode 100644 index 0000000000000..f1f53a7302d6d --- /dev/null +++ b/src/notation/qml/MuseScore/NotationScene/internal/EditStyle/HammerOnPullOffTappingPage.qml @@ -0,0 +1,169 @@ +/* + * SPDX-License-Identifier: GPL-3.0-only + * MuseScore-CLA-applies + * + * MuseScore + * Music Composition & Notation + * + * Copyright (C) 2021 MuseScore BVBA and others + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +import QtQuick 2.15 +import QtQuick.Layouts 1.15 +import QtQuick.Controls + +import MuseScore.NotationScene 1.0 +import Muse.UiComponents 1.0 +import Muse.Ui 1.0 + +StyledFlickable { + id: root + + signal goToTextStylePage(string s) + + contentWidth: column.width + contentHeight: column.height + + HammerOnPullOffTappingPageModel { + id: hopoPage + } + + ColumnLayout { + id: column + spacing: 12 + + StyledGroupBox { + Layout.fillWidth: true + Layout.minimumWidth: 500 + title: qsTrc("notation/editstyle/hammeronpulloff", "Visibility") + + ColumnLayout { + width: parent.width + spacing: 10 + + StyledTextLabel { + text: qsTrc("notation/editstyle/hammeronpulloff", "Show 'H' and 'P' symbols on") + } + + RowLayout { + ToggleButton { + checked: hopoPage.showOnStandardStaves.value === true + onToggled: { + hopoPage.showOnStandardStaves.value = !hopoPage.showOnStandardStaves.value + } + } + + StyledTextLabel { + Layout.fillWidth: true + horizontalAlignment: Text.AlignLeft + text: "Standard staves" + } + } + + RowLayout { + ToggleButton { + checked: hopoPage.showOnTabStaves.value === true + onToggled: { + hopoPage.showOnTabStaves.value = !hopoPage.showOnTabStaves.value + } + } + + StyledTextLabel { + id : toggleText + Layout.fillWidth: true + horizontalAlignment: Text.AlignLeft + text: "Tablature staves" + } + } + } + } + + StyledGroupBox { + Layout.fillWidth: true + Layout.minimumWidth: 500 + title: qsTrc("notation/editstyle/hammeronpulloff", "Case") + + RowLayout { + width: parent.width + spacing: 10 + + RadioButtonGroup { + orientation: ListView.Horizontal + spacing: 6 + + model: [ + {text: qsTrc("notation/editstyle/hammeronpulloff", "HP"), value: true }, + {text: qsTrc("notation/editstyle/hammeronpulloff", "hp"), value: false }, + ] + + delegate: FlatRadioButton { + width: 40 + height: 30 + text: modelData.text + checked: hopoPage.hopoUpperCase.value === modelData.value + onToggled: hopoPage.hopoUpperCase.value = modelData.value + } + } + + SeparatorLine { + orientation: Qt.Vertical + } + + FlatButton { + text: qsTrc("notation", "Edit text style") + + onClicked: { + root.goToTextStylePage("hammer-ons-pull-offs-and-tapping") + } + } + + Item { + Layout.fillWidth: true + } + } + } + + StyledGroupBox { + Layout.fillWidth: true + Layout.minimumWidth: 500 + title: qsTrc("notation/editstyle/hammeronpulloff", "Consecutive hammer-on/pull-offs") + + ColumnLayout { + width: parent.width + spacing: 10 + + RadioButtonGroup { + orientation: ListView.Vertical + spacing: 6 + + model: [ + {text: qsTrc("notation/editstyle/hammeronpulloff", "Show 'H' or 'P' between each pair of notes"), value: true }, + {text: qsTrc("notation/editstyle/hammeronpulloff", "Show 'H' or 'P' once per group of ascending/descending notes"), value: false }, + ] + + delegate: RoundedRadioButton { + text: modelData.text + checked: hopoPage.hopoShowAll.value === modelData.value + onToggled: hopoPage.hopoShowAll.value = modelData.value + } + } + + StyledImage { + forceHeight: 180 + source: hopoPage.hopoShowAll.value === true ? "hammerOnPullOffImages/hopoShowAll" : "hammerOnPullOffImages/hopoNotShowAll" + } + } + } + } +} diff --git a/src/notation/qml/MuseScore/NotationScene/internal/EditStyle/hammerOnPullOffImages/hopoNotShowAll.png b/src/notation/qml/MuseScore/NotationScene/internal/EditStyle/hammerOnPullOffImages/hopoNotShowAll.png new file mode 100644 index 0000000000000000000000000000000000000000..bcccaf5f3fb1967284a6c42dfee005e730929f6e GIT binary patch literal 72633 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

;5k&aCWn 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 Kht68P^Zx?=RD2Ks literal 0 HcmV?d00001 diff --git a/src/notation/qml/MuseScore/NotationScene/internal/EditStyle/hammerOnPullOffImages/hopoShowAll.png b/src/notation/qml/MuseScore/NotationScene/internal/EditStyle/hammerOnPullOffImages/hopoShowAll.png new file mode 100644 index 0000000000000000000000000000000000000000..efb09a74f8da97808a02a2fb7f2f9b7cb6dabc07 GIT binary patch literal 82219 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 sgvWb. + */ +#include "hammeronpullofftappingpagemodel.h" + +using namespace mu::notation; +using namespace mu::engraving; + +HammerOnPullOffTappingPageModel::HammerOnPullOffTappingPageModel(QObject* parent) + : AbstractStyleDialogModel(parent, { StyleId::hopoShowOnStandardStaves, + StyleId::hopoShowOnTabStaves, + StyleId::hopoUpperCase, + StyleId::hopoShowAll }) +{ +} + +StyleItem* HammerOnPullOffTappingPageModel::showOnStandardStaves() const +{ + return styleItem(StyleId::hopoShowOnStandardStaves); +} + +StyleItem* HammerOnPullOffTappingPageModel::showOnTabStaves() const +{ + return styleItem(StyleId::hopoShowOnTabStaves); +} + +StyleItem* HammerOnPullOffTappingPageModel::hopoUpperCase() const +{ + return styleItem(StyleId::hopoUpperCase); +} + +StyleItem* HammerOnPullOffTappingPageModel::hopoShowAll() const +{ + return styleItem(StyleId::hopoShowAll); +} diff --git a/src/notation/view/styledialog/hammeronpullofftappingpagemodel.h b/src/notation/view/styledialog/hammeronpullofftappingpagemodel.h new file mode 100644 index 0000000000000..36eb01143fbf7 --- /dev/null +++ b/src/notation/view/styledialog/hammeronpullofftappingpagemodel.h @@ -0,0 +1,45 @@ +/* + * SPDX-License-Identifier: GPL-3.0-only + * MuseScore-CLA-applies + * + * MuseScore + * Music Composition & Notation + * + * Copyright (C) 2021 MuseScore BVBA and others + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "abstractstyledialogmodel.h" + +namespace mu::notation { +class HammerOnPullOffTappingPageModel : public AbstractStyleDialogModel +{ + Q_OBJECT + + Q_PROPERTY(StyleItem * showOnStandardStaves READ showOnStandardStaves CONSTANT) + Q_PROPERTY(StyleItem * showOnTabStaves READ showOnTabStaves CONSTANT) + Q_PROPERTY(StyleItem * hopoUpperCase READ hopoUpperCase CONSTANT) + Q_PROPERTY(StyleItem * hopoShowAll READ hopoShowAll CONSTANT) + +public: + explicit HammerOnPullOffTappingPageModel(QObject* parent = nullptr); + + StyleItem* showOnStandardStaves() const; + StyleItem* showOnTabStaves() const; + StyleItem* hopoUpperCase() const; + StyleItem* hopoShowAll() const; +}; +} diff --git a/src/notation/view/styledialog/styledialog.cmake b/src/notation/view/styledialog/styledialog.cmake index 6bfc437653e04..10436b6c2ea9e 100644 --- a/src/notation/view/styledialog/styledialog.cmake +++ b/src/notation/view/styledialog/styledialog.cmake @@ -24,4 +24,6 @@ set(STYLEDIALOG_SRC ${CMAKE_CURRENT_LIST_DIR}/notelinesectionmodel.h ${CMAKE_CURRENT_LIST_DIR}/clefkeytimesigpagemodel.cpp ${CMAKE_CURRENT_LIST_DIR}/clefkeytimesigpagemodel.h + ${CMAKE_CURRENT_LIST_DIR}/hammeronpullofftappingpagemodel.cpp + ${CMAKE_CURRENT_LIST_DIR}/hammeronpullofftappingpagemodel.h ) diff --git a/src/notation/view/widgets/editstyle.cpp b/src/notation/view/widgets/editstyle.cpp index 43bbc118a4f77..518e55d20584a 100644 --- a/src/notation/view/widgets/editstyle.cpp +++ b/src/notation/view/widgets/editstyle.cpp @@ -87,6 +87,7 @@ static const QStringList ALL_PAGE_CODES { "text-line", "system-text-line", "articulations-and-ornaments", + "hammer-ons-pull-offs-and-tapping", "fermatas", "staff-text", "tempo-text", @@ -138,6 +139,7 @@ static const QStringList ALL_TEXT_STYLE_SUBPAGE_CODES { "fingering", "lh-guitar-fingering", "rh-guitar-fingering", + "hammer-ons-pull-offs-and-tapping", "string-number", "string-tunings", "fretboard-diagram-fingering", @@ -885,8 +887,8 @@ EditStyle::EditStyle(QWidget* parent) // Define string here instead of in the .ui file to avoid MSVC compiler warning C4125, which would // be triggered by the decimal digit immediately following a non-ASCII character (curly quote). - oneMeasureRepeatShow1->setText(muse::qtrc("EditStyleBase", "Show ‘1’ on 1-measure repeats")); - singleMMRestShowNumber->setText(muse::qtrc("EditStyleBase", "Show number ‘1’")); + oneMeasureRepeatShow1->setText(muse::qtrc("EditStyleBase", "Show ‘1’ on 1-measure repeats")); + singleMMRestShowNumber->setText(muse::qtrc("EditStyleBase", "Show number ‘1’")); // ==================================================== // BEAMS (QML) @@ -939,6 +941,17 @@ EditStyle::EditStyle(QWidget* parent) connect(fretboardsPage.view->rootObject(), SIGNAL(goToTextStylePage(QString)), this, SLOT(goToTextStylePage(QString))); fretboardsWidget->layout()->addWidget(fretboardsPage.widget); + // ==================================================== + // Hammer-on/pull-off and tapping STYLE PAGE (QML) + // ==================================================== + + auto hoposTappingPage = createQmlWidget( + hoposPageWidget, + QUrl(QString::fromUtf8("qrc:/qml/MuseScore/NotationScene/internal/EditStyle/HammerOnPullOffTappingPage.qml"))); + hoposTappingPage.widget->setMinimumSize(224, 400); + connect(hoposTappingPage.view->rootObject(), SIGNAL(goToTextStylePage(QString)), this, SLOT(goToTextStylePage(QString))); + hoposPageWidget->layout()->addWidget(hoposTappingPage.widget); + // ==================================================== // GLISSANDO STYLE SECTION (QML) // ==================================================== @@ -1446,7 +1459,7 @@ void EditStyle::setHeaderFooterToolTip() + QString("

") + muse::qtrc("notation/editstyle", "Available metadata tags and their current values") + QString("
") - + muse::qtrc("notation/editstyle", "(in File > Project properties…):") + + muse::qtrc("notation/editstyle", "(in File > Project properties…):") + QString("

"); // show all tags for current score/part @@ -1617,6 +1630,8 @@ QString EditStyle::pageCodeForElement(const EngravingItem* element) case ElementType::LAISSEZ_VIB_SEGMENT: case ElementType::PARTIAL_TIE: case ElementType::PARTIAL_TIE_SEGMENT: + case ElementType::HAMMER_ON_PULL_OFF: + case ElementType::HAMMER_ON_PULL_OFF_SEGMENT: return "slurs-and-ties"; case ElementType::HAIRPIN: diff --git a/src/notation/view/widgets/editstyle.ui b/src/notation/view/widgets/editstyle.ui index 3a47445722431..abaa9f0d5668f 100644 --- a/src/notation/view/widgets/editstyle.ui +++ b/src/notation/view/widgets/editstyle.ui @@ -199,6 +199,11 @@ Articulations & ornaments + + + Hammer-ons, pull-offs & tapping + + Fermatas @@ -11465,6 +11470,15 @@ + + + + + + + + + @@ -14211,8 +14225,8 @@ first note of the system 0 0 - 98 - 28 + 624 + 478 diff --git a/src/notation/view/widgets/timeline.cpp b/src/notation/view/widgets/timeline.cpp index 0fe86a0977fab..74d4eba6b5874 100644 --- a/src/notation/view/widgets/timeline.cpp +++ b/src/notation/view/widgets/timeline.cpp @@ -1966,6 +1966,7 @@ void Timeline::drawSelection() case ElementType::SLUR_SEGMENT: case ElementType::TIE: case ElementType::SLUR: + case ElementType::HAMMER_ON_PULL_OFF: continue; break; default: break; diff --git a/src/palette/internal/palettecreator.cpp b/src/palette/internal/palettecreator.cpp index 05dc7af9f8dcb..7d019f3d9fecf 100644 --- a/src/palette/internal/palettecreator.cpp +++ b/src/palette/internal/palettecreator.cpp @@ -51,6 +51,7 @@ #include "engraving/dom/gradualtempochange.h" #include "engraving/dom/guitarbend.h" #include "engraving/dom/hairpin.h" +#include "engraving/dom/hammeronpulloff.h" #include "engraving/dom/harppedaldiagram.h" #include "engraving/dom/instrchange.h" #include "engraving/dom/jump.h" @@ -1603,8 +1604,8 @@ PalettePtr PaletteCreator::newTextPalette(bool defaultPalette) sp->appendElement(meaNum, QT_TRANSLATE_NOOP("palette", "Measure number"))->setElementTranslated(true); static const std::vector playTechAnnotationsMaster = { - { QT_TRANSLATE_NOOP("palette", "détaché"), PlayingTechniqueType::Detache }, - { QT_TRANSLATE_NOOP("palette", "martelé"), PlayingTechniqueType::Martele }, + { QT_TRANSLATE_NOOP("palette", "détaché"), PlayingTechniqueType::Detache }, + { QT_TRANSLATE_NOOP("palette", "martelé"), PlayingTechniqueType::Martele }, { QT_TRANSLATE_NOOP("palette", "col legno"), PlayingTechniqueType::ColLegno }, { QT_TRANSLATE_NOOP("palette", "sul pont."), PlayingTechniqueType::SulPonticello }, { QT_TRANSLATE_NOOP("palette", "sul tasto"), PlayingTechniqueType::SulTasto }, @@ -1762,7 +1763,7 @@ PalettePtr PaletteCreator::newGuitarPalette(bool defaultPalette) capoLine->setLen(w); capoLine->setBeginText(u"VII"); capoLine->setEndHookType(HookType::HOOK_90); - sp->appendElement(capoLine, QT_TRANSLATE_NOOP("palette", "Barré line"), 0.8); + sp->appendElement(capoLine, QT_TRANSLATE_NOOP("palette", "Barré line"), 0.8); auto pm = makeElement(gpaletteScore); pm->setLen(w); @@ -1826,6 +1827,9 @@ PalettePtr PaletteCreator::newGuitarPalette(bool defaultPalette) sp->appendElement(f, QT_TRANSLATE_NOOP("palette", "String number %1")); } + auto hopo = Factory::makeHammerOnPullOff(gpaletteScore->dummy()); + sp->appendElement(hopo, QT_TRANSLATE_NOOP("palette", "Hammer-on / pull-off"), 0.8); + static const SymIdList luteSymbols { SymId::stringsThumbPosition, SymId::luteFingeringRHThumb, SymId::luteFingeringRHFirst, From eef9b7ac35a6f7c3d14d5dc4752268274edf4e6e Mon Sep 17 00:00:00 2001 From: Michele Spagnolo Date: Fri, 23 May 2025 12:24:23 +0200 Subject: [PATCH 02/13] corrections --- src/engraving/rendering/single/singledraw.cpp | 2 +- .../internal/EditStyle/HammerOnPullOffTappingPage.qml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/engraving/rendering/single/singledraw.cpp b/src/engraving/rendering/single/singledraw.cpp index 727f11d6e13a2..4fa9858695beb 100644 --- a/src/engraving/rendering/single/singledraw.cpp +++ b/src/engraving/rendering/single/singledraw.cpp @@ -1774,7 +1774,7 @@ void SingleDraw::draw(const HammerOnPullOffSegment* item, muse::draw::Painter* p draw(toSlurSegment(item), painter); } -void SingleDraw::draw(const HammerOnPullOffText *item, Painter *painter) +void SingleDraw::draw(const HammerOnPullOffText* item, Painter* painter) { drawTextBase(item, painter); } diff --git a/src/notation/qml/MuseScore/NotationScene/internal/EditStyle/HammerOnPullOffTappingPage.qml b/src/notation/qml/MuseScore/NotationScene/internal/EditStyle/HammerOnPullOffTappingPage.qml index f1f53a7302d6d..d2c70f3ac1a09 100644 --- a/src/notation/qml/MuseScore/NotationScene/internal/EditStyle/HammerOnPullOffTappingPage.qml +++ b/src/notation/qml/MuseScore/NotationScene/internal/EditStyle/HammerOnPullOffTappingPage.qml @@ -53,7 +53,7 @@ StyledFlickable { spacing: 10 StyledTextLabel { - text: qsTrc("notation/editstyle/hammeronpulloff", "Show 'H' and 'P' symbols on") + text: qsTrc("notation/editstyle/hammeronpulloff", "Show ‘H’ and ‘P’ symbols on") } RowLayout { @@ -148,8 +148,8 @@ StyledFlickable { spacing: 6 model: [ - {text: qsTrc("notation/editstyle/hammeronpulloff", "Show 'H' or 'P' between each pair of notes"), value: true }, - {text: qsTrc("notation/editstyle/hammeronpulloff", "Show 'H' or 'P' once per group of ascending/descending notes"), value: false }, + {text: qsTrc("notation/editstyle/hammeronpulloff", "Show ‘H’ or ‘P’ between each pair of notes"), value: true }, + {text: qsTrc("notation/editstyle/hammeronpulloff", "Show ‘H’ or ‘P’ once per group of ascending/descending notes"), value: false }, ] delegate: RoundedRadioButton { From 9f4a37c30c7cf7e60b41a2e3d916d0ce92e0d0fc Mon Sep 17 00:00:00 2001 From: Michele Spagnolo Date: Mon, 26 May 2025 15:22:50 +0200 Subject: [PATCH 03/13] Set default HOPO text style to 8pt --- src/engraving/style/styledef.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engraving/style/styledef.cpp b/src/engraving/style/styledef.cpp index b72adee405656..d26e9f9cf79e1 100644 --- a/src/engraving/style/styledef.cpp +++ b/src/engraving/style/styledef.cpp @@ -898,7 +898,7 @@ const std::array StyleDef::styleValue styleDef(rhGuitarFingeringOffset, PointF()), styleDef(hammerOnPullOffTappingFontFace, "Edwin"), - styleDef(hammerOnPullOffTappingFontSize, 10.0), + styleDef(hammerOnPullOffTappingFontSize, 8.0), styleDef(hammerOnPullOffTappingLineSpacing, 1.0), styleDef(hammerOnPullOffTappingFontSpatiumDependent, true), styleDef(hammerOnPullOffTappingFontStyle, int(FontStyle::Normal)), From 6cde4e3c6125e690b1dfde9ea50bc8e316b47ebc Mon Sep 17 00:00:00 2001 From: Michele Spagnolo Date: Mon, 26 May 2025 17:16:58 +0200 Subject: [PATCH 04/13] Property panel show Slur setting on select Hopo --- src/inspector/models/abstractinspectormodel.cpp | 2 ++ src/inspector/models/abstractinspectormodel.h | 1 + src/inspector/models/inspectormodelcreator.cpp | 2 ++ .../models/notation/lines/slurandtiesettingsmodel.cpp | 5 +++++ .../models/notation/lines/slurandtiesettingsmodel.h | 3 ++- .../Inspector/notation/NotationInspectorSectionLoader.qml | 3 ++- 6 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/inspector/models/abstractinspectormodel.cpp b/src/inspector/models/abstractinspectormodel.cpp index b1c0145943671..7bb8cafecada2 100644 --- a/src/inspector/models/abstractinspectormodel.cpp +++ b/src/inspector/models/abstractinspectormodel.cpp @@ -50,6 +50,8 @@ static const QMap NOTATION_ELEME { mu::engraving::ElementType::VIBRATO_SEGMENT, InspectorModelType::TYPE_VIBRATO }, { mu::engraving::ElementType::SLUR, InspectorModelType::TYPE_SLUR }, { mu::engraving::ElementType::SLUR_SEGMENT, InspectorModelType::TYPE_SLUR }, + { mu::engraving::ElementType::HAMMER_ON_PULL_OFF, InspectorModelType::TYPE_HAMMER_ON_PULL_OFF }, + { mu::engraving::ElementType::HAMMER_ON_PULL_OFF_SEGMENT, InspectorModelType::TYPE_HAMMER_ON_PULL_OFF }, { mu::engraving::ElementType::TIE, InspectorModelType::TYPE_TIE }, { mu::engraving::ElementType::TIE_SEGMENT, InspectorModelType::TYPE_TIE }, { mu::engraving::ElementType::LAISSEZ_VIB, InspectorModelType::TYPE_LAISSEZ_VIB }, diff --git a/src/inspector/models/abstractinspectormodel.h b/src/inspector/models/abstractinspectormodel.h index 9eb9ad563cf32..181e98b27682f 100644 --- a/src/inspector/models/abstractinspectormodel.h +++ b/src/inspector/models/abstractinspectormodel.h @@ -104,6 +104,7 @@ class AbstractInspectorModel : public QObject, public muse::async::Asyncable TYPE_VOLTA, TYPE_VIBRATO, TYPE_SLUR, + TYPE_HAMMER_ON_PULL_OFF, TYPE_TIE, TYPE_LAISSEZ_VIB, TYPE_PARTIAL_TIE, diff --git a/src/inspector/models/inspectormodelcreator.cpp b/src/inspector/models/inspectormodelcreator.cpp index c6997a0b6977a..8e314cc174ef0 100644 --- a/src/inspector/models/inspectormodelcreator.cpp +++ b/src/inspector/models/inspectormodelcreator.cpp @@ -156,6 +156,8 @@ AbstractInspectorModel* InspectorModelCreator::newInspectorModel(InspectorModelT return new SlurAndTieSettingsModel(parent, repository, SlurAndTieSettingsModel::LaissezVib); case InspectorModelType::TYPE_PARTIAL_TIE: return new SlurAndTieSettingsModel(parent, repository, SlurAndTieSettingsModel::PartialTie); + case InspectorModelType::TYPE_HAMMER_ON_PULL_OFF: + return new SlurAndTieSettingsModel(parent, repository, SlurAndTieSettingsModel::HammerOnPullOff); case InspectorModelType::TYPE_STAFF_TYPE_CHANGES: return new StaffTypeSettingsModel(parent, repository); case InspectorModelType::TYPE_TEXT_FRAME: diff --git a/src/inspector/models/notation/lines/slurandtiesettingsmodel.cpp b/src/inspector/models/notation/lines/slurandtiesettingsmodel.cpp index 22f69a33808b4..a883a1076c3e6 100644 --- a/src/inspector/models/notation/lines/slurandtiesettingsmodel.cpp +++ b/src/inspector/models/notation/lines/slurandtiesettingsmodel.cpp @@ -58,6 +58,11 @@ SlurAndTieSettingsModel::SlurAndTieSettingsModel(QObject* parent, IElementReposi setElementType(mu::engraving::ElementType::PARTIAL_TIE); setTitle(muse::qtrc("inspector", "Tie (partial)")); setIcon(IconCode::NOTE_TIE); + } else if (elementType == ElementType::HammerOnPullOff) { + setModelType(InspectorModelType::TYPE_HAMMER_ON_PULL_OFF); + setElementType(mu::engraving::ElementType::HAMMER_ON_PULL_OFF); + setTitle(muse::qtrc("inspector", "Hammer-on/pull-off")); + setIcon(IconCode::NOTE_SLUR); } createProperties(); diff --git a/src/inspector/models/notation/lines/slurandtiesettingsmodel.h b/src/inspector/models/notation/lines/slurandtiesettingsmodel.h index d8be1cd2de911..0044f6afac2d1 100644 --- a/src/inspector/models/notation/lines/slurandtiesettingsmodel.h +++ b/src/inspector/models/notation/lines/slurandtiesettingsmodel.h @@ -42,7 +42,8 @@ class SlurAndTieSettingsModel : public AbstractInspectorModel Slur, Tie, LaissezVib, - PartialTie + PartialTie, + HammerOnPullOff, }; explicit SlurAndTieSettingsModel(QObject* parent, IElementRepositoryService* repository, ElementType elementType); diff --git a/src/inspector/view/qml/MuseScore/Inspector/notation/NotationInspectorSectionLoader.qml b/src/inspector/view/qml/MuseScore/Inspector/notation/NotationInspectorSectionLoader.qml index 926eec64b4908..83ed977a52016 100644 --- a/src/inspector/view/qml/MuseScore/Inspector/notation/NotationInspectorSectionLoader.qml +++ b/src/inspector/view/qml/MuseScore/Inspector/notation/NotationInspectorSectionLoader.qml @@ -93,7 +93,8 @@ Loader { case Inspector.TYPE_SLUR: case Inspector.TYPE_TIE: case Inspector.TYPE_LAISSEZ_VIB: - case Inspector.TYPE_PARTIAL_TIE: return slurAndTieComp + case Inspector.TYPE_PARTIAL_TIE: + case Inspector.TYPE_HAMMER_ON_PULL_OFF: return slurAndTieComp case Inspector.TYPE_TEMPO: return tempoComp case Inspector.TYPE_A_TEMPO: return aTempoComp case Inspector.TYPE_TEMPO_PRIMO: return tempoPrimoComp From 70d2bdfd6d1b34087dcdd37fb3c3c7ddfdd62ff2 Mon Sep 17 00:00:00 2001 From: Michele Spagnolo Date: Mon, 26 May 2025 18:04:29 +0200 Subject: [PATCH 05/13] Implement keyboard navigation --- src/engraving/dom/navigate.cpp | 22 ++++++++++++++++++++++ src/engraving/dom/segment.cpp | 24 ++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/src/engraving/dom/navigate.cpp b/src/engraving/dom/navigate.cpp index b09b67758642b..233b4a96ae020 100644 --- a/src/engraving/dom/navigate.cpp +++ b/src/engraving/dom/navigate.cpp @@ -25,6 +25,7 @@ #include "chord.h" #include "engravingitem.h" #include "lyrics.h" +#include "hammeronpulloff.h" #include "measure.h" #include "measurerepeat.h" #include "marker.h" @@ -750,6 +751,15 @@ EngravingItem* Score::nextElement() return score()->firstElement(); } } + case ElementType::HAMMER_ON_PULL_OFF_TEXT: + return toHammerOnPullOffText(e)->endChord()->upNote(); + case ElementType::HAMMER_ON_PULL_OFF_SEGMENT: + { + HammerOnPullOffSegment* hopoSeg = toHammerOnPullOffSegment(e); + if (!hopoSeg->hopoText().empty()) { + return hopoSeg->hopoText().front(); + } // else fallthrough: + } case ElementType::VOLTA_SEGMENT: case ElementType::SLUR_SEGMENT: case ElementType::TEXTLINE_SEGMENT: @@ -941,8 +951,20 @@ EngravingItem* Score::prevElement() return previousElement; } + case ElementType::HAMMER_ON_PULL_OFF_TEXT: + { + HammerOnPullOffText* hopoText = toHammerOnPullOffText(e); + HammerOnPullOffSegment* hopoSegment = toHammerOnPullOffSegment(hopoText->parent()); + DO_ASSERT(hopoSegment); + if (hopoSegment->hopoText().size() > 0 && hopoSegment->hopoText().front() == hopoText) { + return hopoSegment; + } else { + return hopoText->startChord()->downNote(); + } + } case ElementType::VOLTA_SEGMENT: case ElementType::SLUR_SEGMENT: + case ElementType::HAMMER_ON_PULL_OFF_SEGMENT: case ElementType::TEXTLINE_SEGMENT: case ElementType::HAIRPIN_SEGMENT: case ElementType::OTTAVA_SEGMENT: diff --git a/src/engraving/dom/segment.cpp b/src/engraving/dom/segment.cpp index 6aebca8352f7b..34303bea8deaa 100644 --- a/src/engraving/dom/segment.cpp +++ b/src/engraving/dom/segment.cpp @@ -37,6 +37,7 @@ #include "chordrest.h" #include "clef.h" #include "engravingitem.h" +#include "hammeronpulloff.h" #include "harppedaldiagram.h" #include "hook.h" #include "instrchange.h" @@ -2102,6 +2103,17 @@ EngravingItem* Segment::nextElement(staff_idx_t activeStaff) if (s) { return s->spannerSegments().front(); } + + for (SpannerSegment* spannerSeg : system()->spannerSegments()) { + if (spannerSeg->staffIdx() == activeStaff && spannerSeg->isHammerOnPullOffSegment()) { + for (HammerOnPullOffText* hopoText : toHammerOnPullOffSegment(spannerSeg)->hopoText()) { + if (hopoText->startChord() && hopoText->startChord()->segment() == this) { + return hopoText; + } + } + } + } + Segment* nextSegment = seg->next1MMenabled(); for (; nextSegment && nextSegment->isTimeTickType(); nextSegment = nextSegment->next1MMenabled()) { if (EngravingItem* annotation = nextSegment->firstAnnotation(activeStaff)) { @@ -2288,7 +2300,19 @@ EngravingItem* Segment::prevElement(staff_idx_t activeStaff) } else { return prev; } + } else { + System* system = seg->system(); + for (SpannerSegment* spannerSeg : system->spannerSegments()) { + if (spannerSeg->staffIdx() == activeStaff && spannerSeg->isHammerOnPullOffSegment()) { + for (HammerOnPullOffText* hopoText : toHammerOnPullOffSegment(spannerSeg)->hopoText()) { + if (hopoText->endChord() && hopoText->endChord()->segment() == seg) { + return hopoText; + } + } + } + } } + Segment* prevSeg = seg->prev1MMenabled(); for (; prevSeg && prevSeg->isTimeTickType(); prevSeg = prevSeg->prev1MMenabled()) { if (Spanner* spanner = prevSeg->lastSpanner(activeStaff)) { From eaa4eb38e53620992a4ae2f6d2f2d997086fc0eb Mon Sep 17 00:00:00 2001 From: Michele Spagnolo Date: Tue, 27 May 2025 08:46:28 +0200 Subject: [PATCH 06/13] Replace slur with hopo if inserted on top --- src/notation/internal/notationinteraction.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/notation/internal/notationinteraction.cpp b/src/notation/internal/notationinteraction.cpp index 939349f32e094..95f3ea6e80d24 100644 --- a/src/notation/internal/notationinteraction.cpp +++ b/src/notation/internal/notationinteraction.cpp @@ -2155,6 +2155,8 @@ bool NotationInteraction::applyPaletteElement(mu::engraving::EngravingItem* elem if (element->isSlur() || cr1->staffIdx() == cr2->staffIdx()) { addSingle = true; } + } else if (sel.element() && sel.element()->isSlurSegment() && element->isHammerOnPullOff()) { + addSingle = true; } auto isEntryDrumStaff = [score]() { @@ -2682,6 +2684,9 @@ void NotationInteraction::doAddSlur(const Slur* slurTemplate) } else if (sel.isSingle()) { if (sel.element()->isNote() && !toNote(sel.element())->isTrillCueNote()) { doAddSlur(toNote(sel.element())->chord(), nullptr, slurTemplate); + } else if (sel.element()->isSlurSegment() && slurTemplate->isHammerOnPullOff()) { + Slur* slur = toSlurSegment(sel.element())->slur(); + doAddSlur(slur->startElement(), slur->endElement(), slurTemplate); } } else { EngravingItem* firstItem = nullptr; @@ -2763,6 +2768,14 @@ void NotationInteraction::doAddSlur(EngravingItem* firstItem, EngravingItem* sec Slur* slur = firstChordRest->slur(secondChordRest); if (!slur || slur->slurDirection() != DirectionV::AUTO) { slur = score()->addSlur(firstChordRest, secondChordRest, slurTemplate); + } else if (slur && slurTemplate->isHammerOnPullOff()) { + // Replace existing slur with HOPO + endEditElement(); + score()->undoRemoveElement(slur); + slur = score()->addSlur(firstChordRest, secondChordRest, slurTemplate); + SlurSegment* segment = slur->frontSegment(); + select({ segment }, SelectType::SINGLE); + startEditGrip(segment, Grip::END); } if (m_noteInput->isNoteInputMode()) { From 8eef92b8a87c6394c1199ad3fa2b637871417b61 Mon Sep 17 00:00:00 2001 From: Michele Spagnolo Date: Tue, 27 May 2025 09:12:30 +0200 Subject: [PATCH 07/13] Allow placement of HopoText independent of Hopo --- src/engraving/dom/edit.cpp | 3 ++- src/engraving/dom/hammeronpulloff.cpp | 11 +++++++++++ src/engraving/dom/hammeronpulloff.h | 2 ++ .../rendering/score/slurtielayout.cpp | 4 ---- src/engraving/rendering/score/systemlayout.cpp | 3 +++ src/engraving/rendering/score/tlayout.cpp | 18 ++++++++++++------ 6 files changed, 30 insertions(+), 11 deletions(-) diff --git a/src/engraving/dom/edit.cpp b/src/engraving/dom/edit.cpp index 5c33f95f94e8c..7446d54c58fa2 100644 --- a/src/engraving/dom/edit.cpp +++ b/src/engraving/dom/edit.cpp @@ -2532,7 +2532,8 @@ void Score::cmdFlip() || e->isPedalSegment() || e->isLyrics() || e->isBreath() - || e->isFermata()) { + || e->isFermata() + || e->isHammerOnPullOffText()) { e->undoChangeProperty(Pid::AUTOPLACE, true); // TODO: undoChangeProperty() should probably do this directly // see https://musescore.org/en/node/281432 diff --git a/src/engraving/dom/hammeronpulloff.cpp b/src/engraving/dom/hammeronpulloff.cpp index 01ae699562070..ea7caab5ada90 100644 --- a/src/engraving/dom/hammeronpulloff.cpp +++ b/src/engraving/dom/hammeronpulloff.cpp @@ -228,6 +228,7 @@ HammerOnPullOffText::HammerOnPullOffText(HammerOnPullOffSegment* parent) : TextBase(ElementType::HAMMER_ON_PULL_OFF_TEXT, parent, TextStyleType::HAMMER_ON_PULL_OFF, ElementFlag::MOVABLE | ElementFlag::GENERATED) { + resetProperty(Pid::PLACEMENT); initElementStyle(&hopoStyle); } @@ -278,4 +279,14 @@ Color HammerOnPullOffText::curColor() const return selected() || parentItem()->selected() ? engravingConf->criticalSelectedColor() : engravingConf->criticalColor(); } + +PropertyValue HammerOnPullOffText::propertyDefault(Pid id) const +{ + switch (id) { + case Pid::PLACEMENT: + return PlacementV::ABOVE; + default: + return TextBase::propertyDefault(id); + } +} } // namespace mu::engraving diff --git a/src/engraving/dom/hammeronpulloff.h b/src/engraving/dom/hammeronpulloff.h index 9a871173dc16d..651d561f3a783 100644 --- a/src/engraving/dom/hammeronpulloff.h +++ b/src/engraving/dom/hammeronpulloff.h @@ -52,6 +52,8 @@ class HammerOnPullOffText final : public TextBase Color curColor() const override; + PropertyValue propertyDefault(Pid id) const override; + private: Chord* m_startChord = nullptr; Chord* m_endChord = nullptr; diff --git a/src/engraving/rendering/score/slurtielayout.cpp b/src/engraving/rendering/score/slurtielayout.cpp index cdd003e9e4c00..c53c5a2a46869 100644 --- a/src/engraving/rendering/score/slurtielayout.cpp +++ b/src/engraving/rendering/score/slurtielayout.cpp @@ -3008,10 +3008,6 @@ void SlurTieLayout::layoutSegment(SlurSegment* item, LayoutContext& ctx, const P ldata->moveY(item->staffOffsetY()); computeBezier(item); - - if (item->isHammerOnPullOffSegment()) { - TLayout::layoutHammerOnPullOffSegment(toHammerOnPullOffSegment(item), ctx); - } } void SlurTieLayout::computeMidThickness(SlurTieSegment* slurTieSeg, double slurTieLengthInSp) diff --git a/src/engraving/rendering/score/systemlayout.cpp b/src/engraving/rendering/score/systemlayout.cpp index 1907861b47772..961322322be26 100644 --- a/src/engraving/rendering/score/systemlayout.cpp +++ b/src/engraving/rendering/score/systemlayout.cpp @@ -1630,6 +1630,9 @@ void SystemLayout::processLines(System* system, LayoutContext& ctx, const std::v continue; } system->staff(stfIdx)->skyline().add(ss->shape().translate(ss->pos())); + if (ss->isHammerOnPullOffSegment()) { + TLayout::layoutHammerOnPullOffSegment(toHammerOnPullOffSegment(ss), ctx); + } } } } diff --git a/src/engraving/rendering/score/tlayout.cpp b/src/engraving/rendering/score/tlayout.cpp index c4a6dbbc85e16..90fd4e39fcfb1 100644 --- a/src/engraving/rendering/score/tlayout.cpp +++ b/src/engraving/rendering/score/tlayout.cpp @@ -3259,12 +3259,13 @@ void TLayout::layoutHammerOnPullOffSegment(HammerOnPullOffSegment* item, LayoutC // The layout of the slur has already been done. Here we layout the H/P letters. item->updateHopoText(); - HammerOnPullOff* hopo = item->hammerOnPullOff(); - Shape hopoSegmentShape = item->mutldata()->shape(); + Skyline& sk = item->system()->staff(item->staffIdx())->skyline(); for (HammerOnPullOffText* hopoText : item->hopoText()) { + bool above = hopoText->placeAbove(); + Align align; - align.vertical = hopo->up() ? AlignV::BASELINE : AlignV::TOP; + align.vertical = above ? AlignV::BASELINE : AlignV::TOP; align.horizontal = AlignH::HCENTER; hopoText->setAlign(align); layoutItem(hopoText, ctx); @@ -3275,13 +3276,18 @@ void TLayout::layoutHammerOnPullOffSegment(HammerOnPullOffSegment* item, LayoutC double vertPadding = 0.5 * item->spatium(); Shape hopoTextShape = hopoText->ldata()->shape().translated(PointF(centerX, 0.0)); - double y = hopo->up() ? -hopoTextShape.minVerticalDistance(hopoSegmentShape) : hopoSegmentShape.minVerticalDistance(hopoTextShape); - y += hopo->up() ? -vertPadding : vertPadding; - y = hopo->up() ? std::min(y, -vertPadding) : std::max(y, item->staff()->staffHeight(item->tick()) + vertPadding); + SkylineLine& skyline = above ? sk.north() : sk.south(); + double y = above ? -skyline.minDistanceToShapeAbove(hopoTextShape) : skyline.minDistanceToShapeBelow(hopoTextShape); + y += above ? -vertPadding : vertPadding; + y = above ? std::min(y, -vertPadding) : std::max(y, item->staff()->staffHeight(item->tick()) + vertPadding); hopoText->mutldata()->setPos(centerX, y); + + hopoTextShape.translateY(y); + skyline.add(hopoTextShape); } + Shape hopoSegmentShape = item->mutldata()->shape(); for (HammerOnPullOffText* hopoText : item->hopoText()) { hopoSegmentShape.add(hopoText->ldata()->shape().translated(hopoText->pos())); } From 324abc08bc1103dee9ce85e91d77ae69e59f94be Mon Sep 17 00:00:00 2001 From: Michele Spagnolo Date: Tue, 27 May 2025 11:54:01 +0200 Subject: [PATCH 08/13] Don't let HP text kern vertically in between notes --- src/engraving/rendering/score/tlayout.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/engraving/rendering/score/tlayout.cpp b/src/engraving/rendering/score/tlayout.cpp index 90fd4e39fcfb1..6176059609edb 100644 --- a/src/engraving/rendering/score/tlayout.cpp +++ b/src/engraving/rendering/score/tlayout.cpp @@ -3281,6 +3281,12 @@ void TLayout::layoutHammerOnPullOffSegment(HammerOnPullOffSegment* item, LayoutC y += above ? -vertPadding : vertPadding; y = above ? std::min(y, -vertPadding) : std::max(y, item->staff()->staffHeight(item->tick()) + vertPadding); + Note* startNote = above ? hopoText->startChord()->upNote() : hopoText->startChord()->downNote(); + Note* endNote = above ? hopoText->endChord()->upNote() : hopoText->endChord()->downNote(); + double yNoteLimit = above ? std::min(startNote->y(), endNote->y()) - 2 * vertPadding + : std::max(startNote->y(), endNote->y()) + 2 * vertPadding; + y = above ? std::min(y, yNoteLimit) : std::max(y, yNoteLimit); + hopoText->mutldata()->setPos(centerX, y); hopoTextShape.translateY(y); From 6d6283a3c35a32fb11e8f0fec3872589658b7eb1 Mon Sep 17 00:00:00 2001 From: Michele Spagnolo Date: Tue, 27 May 2025 13:49:40 +0200 Subject: [PATCH 09/13] Handle cross-system hopos correctly --- src/engraving/dom/hammeronpulloff.cpp | 4 ++++ src/engraving/dom/segment.cpp | 11 ++++++++++ src/engraving/dom/segment.h | 1 + src/engraving/rendering/score/tlayout.cpp | 26 ++++++++++++++++++----- 4 files changed, 37 insertions(+), 5 deletions(-) diff --git a/src/engraving/dom/hammeronpulloff.cpp b/src/engraving/dom/hammeronpulloff.cpp index ea7caab5ada90..edb9591cafb98 100644 --- a/src/engraving/dom/hammeronpulloff.cpp +++ b/src/engraving/dom/hammeronpulloff.cpp @@ -101,7 +101,11 @@ void HammerOnPullOffSegment::updateHopoText() EngravingItem* endEl = hammerOnPullOff()->endElement(); endChord = endEl && endEl->isChord() ? toChord(endEl) : nullptr; } else { + // If the segment doesn't end in this system, the endChord is the first chord of next system ChordRest* lastCR = system()->lastChordRest(track()); + if (lastCR) { + lastCR = toChordRest(lastCR->segment()->next1WithElemsOnTrack(track())->element(track())); + } endChord = lastCR && lastCR->isChord() ? toChord(lastCR) : nullptr; } diff --git a/src/engraving/dom/segment.cpp b/src/engraving/dom/segment.cpp index 34303bea8deaa..5bd9d5cfe402c 100644 --- a/src/engraving/dom/segment.cpp +++ b/src/engraving/dom/segment.cpp @@ -359,6 +359,17 @@ Segment* Segment::next1WithElemsOnStaff(staff_idx_t staffIdx, SegmentType segTyp return next; } +Segment* Segment::next1WithElemsOnTrack(track_idx_t trackIdx, SegmentType segType) const +{ + Segment* next = next1(segType); + + while (next && !next->hasElements(trackIdx, trackIdx)) { + next = next->next1(segType); + } + + return next; +} + Segment* Segment::next1MM(SegmentType types) const { for (Segment* s = next1MM(); s; s = s->next1MM()) { diff --git a/src/engraving/dom/segment.h b/src/engraving/dom/segment.h index 3a316c1cf3672..0b0f818845dbe 100644 --- a/src/engraving/dom/segment.h +++ b/src/engraving/dom/segment.h @@ -101,6 +101,7 @@ class Segment final : public EngravingItem Segment* next1(SegmentType) const; Segment* next1ChordRestOrTimeTick() const; Segment* next1WithElemsOnStaff(staff_idx_t staffIdx, SegmentType segType = SegmentType::ChordRest) const; + Segment* next1WithElemsOnTrack(track_idx_t trackIdx, SegmentType segType = SegmentType::ChordRest) const; Segment* next1MM(SegmentType) const; Segment* prev1() const; diff --git a/src/engraving/rendering/score/tlayout.cpp b/src/engraving/rendering/score/tlayout.cpp index 6176059609edb..09e95570d6688 100644 --- a/src/engraving/rendering/score/tlayout.cpp +++ b/src/engraving/rendering/score/tlayout.cpp @@ -3259,7 +3259,9 @@ void TLayout::layoutHammerOnPullOffSegment(HammerOnPullOffSegment* item, LayoutC // The layout of the slur has already been done. Here we layout the H/P letters. item->updateHopoText(); - Skyline& sk = item->system()->staff(item->staffIdx())->skyline(); + System* system = item->system(); + Fraction systemEndTick = system->endTick(); + Skyline& sk = system->staff(item->staffIdx())->skyline(); for (HammerOnPullOffText* hopoText : item->hopoText()) { bool above = hopoText->placeAbove(); @@ -3270,8 +3272,22 @@ void TLayout::layoutHammerOnPullOffSegment(HammerOnPullOffSegment* item, LayoutC hopoText->setAlign(align); layoutItem(hopoText, ctx); - double startX = hopoText->startChord()->systemPos().x() + hopoText->startChord()->upNote()->headWidth(); - double endX = hopoText->endChord()->systemPos().x(); + const Chord* startChord = hopoText->startChord(); + const Chord* endChord = hopoText->endChord(); + double startX = startChord->systemPos().x() + startChord->upNote()->headWidth(); + double endX = startX; + if (endChord->tick() < systemEndTick) { + endX = endChord->systemPos().x(); + } else { + // The last endChord of this segment is in next system. Use end barline instead. + Measure* lastMeas = system->lastMeasure(); + for (Segment* seg = lastMeas->last(); seg; seg = seg->prev()) { + if (seg->isType(SegmentType::BarLineType)) { + endX = seg->systemPos().x(); + break; + } + } + } double centerX = 0.5 * (startX + endX); double vertPadding = 0.5 * item->spatium(); @@ -3281,8 +3297,8 @@ void TLayout::layoutHammerOnPullOffSegment(HammerOnPullOffSegment* item, LayoutC y += above ? -vertPadding : vertPadding; y = above ? std::min(y, -vertPadding) : std::max(y, item->staff()->staffHeight(item->tick()) + vertPadding); - Note* startNote = above ? hopoText->startChord()->upNote() : hopoText->startChord()->downNote(); - Note* endNote = above ? hopoText->endChord()->upNote() : hopoText->endChord()->downNote(); + Note* startNote = above ? startChord->upNote() : startChord->downNote(); + Note* endNote = above ? endChord->upNote() : endChord->downNote(); double yNoteLimit = above ? std::min(startNote->y(), endNote->y()) - 2 * vertPadding : std::max(startNote->y(), endNote->y()) + 2 * vertPadding; y = above ? std::min(y, yNoteLimit) : std::max(y, yNoteLimit); From 498e558ccd7de8b93fab1c51750b82d0de55e704 Mon Sep 17 00:00:00 2001 From: Michele Spagnolo Date: Tue, 27 May 2025 14:07:36 +0200 Subject: [PATCH 10/13] Update MusescoreIcon file and use HP icons --- src/framework/ui/data/MusescoreIcon.ttf | Bin 167260 -> 171092 bytes src/framework/ui/view/iconcodes.h | 3 +++ .../EditStyle/HammerOnPullOffTappingPage.qml | 6 +++--- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/framework/ui/data/MusescoreIcon.ttf b/src/framework/ui/data/MusescoreIcon.ttf index ad2da51b643323af7f360da309cd2bb81fec32b2..ac18ccaa4538e9da8e418aa0af4820e58e25a118 100644 GIT binary patch delta 4935 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 Date: Tue, 27 May 2025 17:30:09 +0200 Subject: [PATCH 11/13] Improve HP centering on stem side --- src/engraving/rendering/score/tlayout.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/engraving/rendering/score/tlayout.cpp b/src/engraving/rendering/score/tlayout.cpp index 09e95570d6688..439997cbcd7ce 100644 --- a/src/engraving/rendering/score/tlayout.cpp +++ b/src/engraving/rendering/score/tlayout.cpp @@ -3288,6 +3288,10 @@ void TLayout::layoutHammerOnPullOffSegment(HammerOnPullOffSegment* item, LayoutC } } } + if (startChord->stem() && endChord->stem() && startChord->up() == above && endChord->up() == above) { + // Mid-way between centered on the notes and centered on the stems + endX += (above ? 0.5 : -0.5) * endChord->upNote()->headWidth(); + } double centerX = 0.5 * (startX + endX); double vertPadding = 0.5 * item->spatium(); From 897f47e131e67d9ad18320f153893fe636d4fbc8 Mon Sep 17 00:00:00 2001 From: Michele Spagnolo Date: Wed, 28 May 2025 14:20:37 +0200 Subject: [PATCH 12/13] Fix utests --- .../musicxml/internal/musicxml/import/importmusicxmlpass1.cpp | 1 + src/importexport/musicxml/tests/data/importTie2_ref.mscx | 1 + src/importexport/musicxml/tests/data/importTie3_ref.mscx | 1 + src/importexport/musicxml/tests/data/importTie4_ref.mscx | 1 + src/importexport/musicxml/tests/data/testDoletOttavas_ref.mscx | 1 + .../data/testDuplicateFermataOnGraceNoteAndMainNote_ref.mscx | 1 + .../musicxml/tests/data/testDuplicateFermataOnGraceNote_ref.mscx | 1 + src/importexport/musicxml/tests/data/testFinaleInstr2_ref.mscx | 1 + src/importexport/musicxml/tests/data/testFinaleInstr_ref.mscx | 1 + src/importexport/musicxml/tests/data/testInferCodaII_ref.mscx | 1 + src/importexport/musicxml/tests/data/testInferSegnoII_ref.mscx | 1 + .../musicxml/tests/data/testInferredCrescLines2_ref.mscx | 1 + .../musicxml/tests/data/testInferredCrescLines_ref.mscx | 1 + .../musicxml/tests/data/testInferredTempoText_ref.mscx | 1 + .../musicxml/tests/data/testSibMetronomeMarks_ref.mscx | 1 + src/importexport/musicxml/tests/data/testSibOttavas_ref.mscx | 1 + src/importexport/musicxml/tests/data/testTitleSwapMu_ref.mscx | 1 + src/importexport/musicxml/tests/data/testTitleSwapSib_ref.mscx | 1 + 18 files changed, 18 insertions(+) diff --git a/src/importexport/musicxml/internal/musicxml/import/importmusicxmlpass1.cpp b/src/importexport/musicxml/internal/musicxml/import/importmusicxmlpass1.cpp index ce6e9315529ce..b73caa5a01042 100644 --- a/src/importexport/musicxml/internal/musicxml/import/importmusicxmlpass1.cpp +++ b/src/importexport/musicxml/internal/musicxml/import/importmusicxmlpass1.cpp @@ -1808,6 +1808,7 @@ static void updateStyles(Score* score, } bool needUseDefaultSize = tid == TextStyleType::HARMONY_ROMAN + || tid == TextStyleType::HAMMER_ON_PULL_OFF || isTitleFrameStyle(tid) || isHarpPedalStyle(tid); diff --git a/src/importexport/musicxml/tests/data/importTie2_ref.mscx b/src/importexport/musicxml/tests/data/importTie2_ref.mscx index ec027a9edba67..d3f78fddd0cec 100644 --- a/src/importexport/musicxml/tests/data/importTie2_ref.mscx +++ b/src/importexport/musicxml/tests/data/importTie2_ref.mscx @@ -47,6 +47,7 @@ 10.25 Times New Roman 10.25 + Times New Roman Times New Roman 10.25 10.25 diff --git a/src/importexport/musicxml/tests/data/importTie3_ref.mscx b/src/importexport/musicxml/tests/data/importTie3_ref.mscx index 64eecebf9222a..9d52b75d19aee 100644 --- a/src/importexport/musicxml/tests/data/importTie3_ref.mscx +++ b/src/importexport/musicxml/tests/data/importTie3_ref.mscx @@ -41,6 +41,7 @@ Times New Roman Times New Roman Times New Roman + Times New Roman Times New Roman Times New Roman Times New Roman diff --git a/src/importexport/musicxml/tests/data/importTie4_ref.mscx b/src/importexport/musicxml/tests/data/importTie4_ref.mscx index 822c1ece89082..686f15e8282aa 100644 --- a/src/importexport/musicxml/tests/data/importTie4_ref.mscx +++ b/src/importexport/musicxml/tests/data/importTie4_ref.mscx @@ -49,6 +49,7 @@ 22 Palatino,serif 22 + Palatino,serif Palatino,serif 22 22 diff --git a/src/importexport/musicxml/tests/data/testDoletOttavas_ref.mscx b/src/importexport/musicxml/tests/data/testDoletOttavas_ref.mscx index 19b26db2da0eb..60fd851fae997 100644 --- a/src/importexport/musicxml/tests/data/testDoletOttavas_ref.mscx +++ b/src/importexport/musicxml/tests/data/testDoletOttavas_ref.mscx @@ -40,6 +40,7 @@ Times New Roman Times New Roman Times New Roman + Times New Roman Times New Roman Times New Roman Times New Roman diff --git a/src/importexport/musicxml/tests/data/testDuplicateFermataOnGraceNoteAndMainNote_ref.mscx b/src/importexport/musicxml/tests/data/testDuplicateFermataOnGraceNoteAndMainNote_ref.mscx index 15572be3b3a4c..7c286bdc05a92 100644 --- a/src/importexport/musicxml/tests/data/testDuplicateFermataOnGraceNoteAndMainNote_ref.mscx +++ b/src/importexport/musicxml/tests/data/testDuplicateFermataOnGraceNoteAndMainNote_ref.mscx @@ -34,6 +34,7 @@ 10 FreeSerif 10 + FreeSerif FreeSerif 10 10 diff --git a/src/importexport/musicxml/tests/data/testDuplicateFermataOnGraceNote_ref.mscx b/src/importexport/musicxml/tests/data/testDuplicateFermataOnGraceNote_ref.mscx index 32109023dd602..278675811580a 100644 --- a/src/importexport/musicxml/tests/data/testDuplicateFermataOnGraceNote_ref.mscx +++ b/src/importexport/musicxml/tests/data/testDuplicateFermataOnGraceNote_ref.mscx @@ -34,6 +34,7 @@ 10 FreeSerif 10 + FreeSerif FreeSerif 10 10 diff --git a/src/importexport/musicxml/tests/data/testFinaleInstr2_ref.mscx b/src/importexport/musicxml/tests/data/testFinaleInstr2_ref.mscx index ca4d03ebaeb96..79e44a2db1311 100644 --- a/src/importexport/musicxml/tests/data/testFinaleInstr2_ref.mscx +++ b/src/importexport/musicxml/tests/data/testFinaleInstr2_ref.mscx @@ -48,6 +48,7 @@ 8.8 Times 8.8 + Times Times 8.8 8.8 diff --git a/src/importexport/musicxml/tests/data/testFinaleInstr_ref.mscx b/src/importexport/musicxml/tests/data/testFinaleInstr_ref.mscx index f0860aedbfc3f..53c6000718138 100644 --- a/src/importexport/musicxml/tests/data/testFinaleInstr_ref.mscx +++ b/src/importexport/musicxml/tests/data/testFinaleInstr_ref.mscx @@ -48,6 +48,7 @@ 7.4 Times 7.4 + Times Times 7.4 7.4 diff --git a/src/importexport/musicxml/tests/data/testInferCodaII_ref.mscx b/src/importexport/musicxml/tests/data/testInferCodaII_ref.mscx index 1847693c4d557..0b3cd3b128f37 100644 --- a/src/importexport/musicxml/tests/data/testInferCodaII_ref.mscx +++ b/src/importexport/musicxml/tests/data/testInferCodaII_ref.mscx @@ -40,6 +40,7 @@ Times New Roman Times New Roman Times New Roman + Times New Roman Times New Roman Times New Roman Times New Roman diff --git a/src/importexport/musicxml/tests/data/testInferSegnoII_ref.mscx b/src/importexport/musicxml/tests/data/testInferSegnoII_ref.mscx index 6a874868bf4f9..28c02eb435531 100644 --- a/src/importexport/musicxml/tests/data/testInferSegnoII_ref.mscx +++ b/src/importexport/musicxml/tests/data/testInferSegnoII_ref.mscx @@ -40,6 +40,7 @@ Times New Roman Times New Roman Times New Roman + Times New Roman Times New Roman Times New Roman Times New Roman diff --git a/src/importexport/musicxml/tests/data/testInferredCrescLines2_ref.mscx b/src/importexport/musicxml/tests/data/testInferredCrescLines2_ref.mscx index b64d2e7f85858..67e8ed72deeaa 100644 --- a/src/importexport/musicxml/tests/data/testInferredCrescLines2_ref.mscx +++ b/src/importexport/musicxml/tests/data/testInferredCrescLines2_ref.mscx @@ -41,6 +41,7 @@ Times New Roman Times New Roman Times New Roman + Times New Roman Times New Roman Times New Roman Times New Roman diff --git a/src/importexport/musicxml/tests/data/testInferredCrescLines_ref.mscx b/src/importexport/musicxml/tests/data/testInferredCrescLines_ref.mscx index c465532a8a5a3..85bd10236ead5 100644 --- a/src/importexport/musicxml/tests/data/testInferredCrescLines_ref.mscx +++ b/src/importexport/musicxml/tests/data/testInferredCrescLines_ref.mscx @@ -41,6 +41,7 @@ Times New Roman Times New Roman Times New Roman + Times New Roman Times New Roman Times New Roman Times New Roman diff --git a/src/importexport/musicxml/tests/data/testInferredTempoText_ref.mscx b/src/importexport/musicxml/tests/data/testInferredTempoText_ref.mscx index 77f4e78d98ce5..8b575bf563146 100644 --- a/src/importexport/musicxml/tests/data/testInferredTempoText_ref.mscx +++ b/src/importexport/musicxml/tests/data/testInferredTempoText_ref.mscx @@ -40,6 +40,7 @@ Times New Roman Times New Roman Times New Roman + Times New Roman Times New Roman Times New Roman Times New Roman diff --git a/src/importexport/musicxml/tests/data/testSibMetronomeMarks_ref.mscx b/src/importexport/musicxml/tests/data/testSibMetronomeMarks_ref.mscx index 6c019acd9499f..d414f4629d8ee 100644 --- a/src/importexport/musicxml/tests/data/testSibMetronomeMarks_ref.mscx +++ b/src/importexport/musicxml/tests/data/testSibMetronomeMarks_ref.mscx @@ -40,6 +40,7 @@ Times New Roman Times New Roman Times New Roman + Times New Roman Times New Roman Times New Roman Times New Roman diff --git a/src/importexport/musicxml/tests/data/testSibOttavas_ref.mscx b/src/importexport/musicxml/tests/data/testSibOttavas_ref.mscx index 40776947325c1..5b0fedbe03646 100644 --- a/src/importexport/musicxml/tests/data/testSibOttavas_ref.mscx +++ b/src/importexport/musicxml/tests/data/testSibOttavas_ref.mscx @@ -57,6 +57,7 @@ 11.9365 Quicksand 11.9365 + Quicksand Quicksand 11.9365 11.9365 diff --git a/src/importexport/musicxml/tests/data/testTitleSwapMu_ref.mscx b/src/importexport/musicxml/tests/data/testTitleSwapMu_ref.mscx index 053703a7f6664..ebf0c97c7cd24 100644 --- a/src/importexport/musicxml/tests/data/testTitleSwapMu_ref.mscx +++ b/src/importexport/musicxml/tests/data/testTitleSwapMu_ref.mscx @@ -42,6 +42,7 @@ 11.9365 Plantin MT Std 11.9365 + Plantin MT Std Plantin MT Std 11.9365 11.9365 diff --git a/src/importexport/musicxml/tests/data/testTitleSwapSib_ref.mscx b/src/importexport/musicxml/tests/data/testTitleSwapSib_ref.mscx index cb1544bafd05a..71df491139488 100644 --- a/src/importexport/musicxml/tests/data/testTitleSwapSib_ref.mscx +++ b/src/importexport/musicxml/tests/data/testTitleSwapSib_ref.mscx @@ -42,6 +42,7 @@ 11.9365 Plantin MT Std 11.9365 + Plantin MT Std Plantin MT Std 11.9365 11.9365 From 7b61a1af451cf106e4c70a72b99ca766fca25fce Mon Sep 17 00:00:00 2001 From: Michele Spagnolo Date: Thu, 29 May 2025 07:50:16 +0200 Subject: [PATCH 13/13] Code review --- src/engraving/rendering/score/tlayout.cpp | 9 ++------- src/engraving/rw/write/twrite.cpp | 3 --- src/engraving/types/typesconv.cpp | 1 - src/notation/view/widgets/editstyle.cpp | 11 +++++++---- 4 files changed, 9 insertions(+), 15 deletions(-) diff --git a/src/engraving/rendering/score/tlayout.cpp b/src/engraving/rendering/score/tlayout.cpp index 439997cbcd7ce..2d6bc118c8418 100644 --- a/src/engraving/rendering/score/tlayout.cpp +++ b/src/engraving/rendering/score/tlayout.cpp @@ -3280,13 +3280,8 @@ void TLayout::layoutHammerOnPullOffSegment(HammerOnPullOffSegment* item, LayoutC endX = endChord->systemPos().x(); } else { // The last endChord of this segment is in next system. Use end barline instead. - Measure* lastMeas = system->lastMeasure(); - for (Segment* seg = lastMeas->last(); seg; seg = seg->prev()) { - if (seg->isType(SegmentType::BarLineType)) { - endX = seg->systemPos().x(); - break; - } - } + Segment* endSeg = system->lastMeasure()->last(SegmentType::BarLineType); + endX = endSeg ? endSeg->systemPos().x() : endX; } if (startChord->stem() && endChord->stem() && startChord->up() == above && endChord->up() == above) { // Mid-way between centered on the notes and centered on the stems diff --git a/src/engraving/rw/write/twrite.cpp b/src/engraving/rw/write/twrite.cpp index 1dab33fb2d12a..9e6cdf3533b81 100644 --- a/src/engraving/rw/write/twrite.cpp +++ b/src/engraving/rw/write/twrite.cpp @@ -1656,9 +1656,6 @@ void TWrite::write(const HammerOnPullOff* item, XmlWriter& xml, WriteContext& ct } xml.startElement(item); - if (ctx.clipboardmode()) { - xml.tag("stemArr", Slur::calcStemArrangement(item->startElement(), item->endElement())); - } writeProperty(item, xml, Pid::PARTIAL_SPANNER_DIRECTION); diff --git a/src/engraving/types/typesconv.cpp b/src/engraving/types/typesconv.cpp index 97e90d7f8a425..2420c7df3d249 100644 --- a/src/engraving/types/typesconv.cpp +++ b/src/engraving/types/typesconv.cpp @@ -289,7 +289,6 @@ static const std::vector > ELEMENT_TYPES = { { ElementType::SYSTEM, "System", muse::TranslatableString("engraving", "System") }, { ElementType::CHORD, "Chord", muse::TranslatableString("engraving", "Chord") }, { ElementType::SLUR, "Slur", muse::TranslatableString("engraving", "Slur") }, - { ElementType::HAMMER_ON_PULL_OFF, "HammerOnPullOff", muse::TranslatableString("engraving", "Hammer-on pull-off") }, { ElementType::HBOX, "HBox", muse::TranslatableString("engraving", "Horizontal frame") }, { ElementType::VBOX, "VBox", muse::TranslatableString("engraving", "Vertical frame") }, { ElementType::TBOX, "TBox", muse::TranslatableString("engraving", "Text frame") }, diff --git a/src/notation/view/widgets/editstyle.cpp b/src/notation/view/widgets/editstyle.cpp index 518e55d20584a..4c5c44326ae69 100644 --- a/src/notation/view/widgets/editstyle.cpp +++ b/src/notation/view/widgets/editstyle.cpp @@ -887,8 +887,8 @@ EditStyle::EditStyle(QWidget* parent) // Define string here instead of in the .ui file to avoid MSVC compiler warning C4125, which would // be triggered by the decimal digit immediately following a non-ASCII character (curly quote). - oneMeasureRepeatShow1->setText(muse::qtrc("EditStyleBase", "Show ‘1’ on 1-measure repeats")); - singleMMRestShowNumber->setText(muse::qtrc("EditStyleBase", "Show number ‘1’")); + oneMeasureRepeatShow1->setText(muse::qtrc("EditStyleBase", "Show ‘1’ on 1-measure repeats")); + singleMMRestShowNumber->setText(muse::qtrc("EditStyleBase", "Show number ‘1’")); // ==================================================== // BEAMS (QML) @@ -1459,7 +1459,7 @@ void EditStyle::setHeaderFooterToolTip() + QString("

") + muse::qtrc("notation/editstyle", "Available metadata tags and their current values") + QString("
") - + muse::qtrc("notation/editstyle", "(in File > Project properties…):") + + muse::qtrc("notation/editstyle", "(in File > Project properties…):") + QString("

"); // show all tags for current score/part @@ -1630,9 +1630,12 @@ QString EditStyle::pageCodeForElement(const EngravingItem* element) case ElementType::LAISSEZ_VIB_SEGMENT: case ElementType::PARTIAL_TIE: case ElementType::PARTIAL_TIE_SEGMENT: + return "slurs-and-ties"; + case ElementType::HAMMER_ON_PULL_OFF: case ElementType::HAMMER_ON_PULL_OFF_SEGMENT: - return "slurs-and-ties"; + case ElementType::HAMMER_ON_PULL_OFF_TEXT: + return "hammer-ons-pull-offs-and-tapping"; case ElementType::HAIRPIN: case ElementType::HAIRPIN_SEGMENT: