From 4ce46ebdda3b62ee05cf89cad6d8f62d7ec2e1e2 Mon Sep 17 00:00:00 2001 From: "J.G" Date: Sun, 29 Dec 2024 11:12:03 +0100 Subject: [PATCH] Allow reversal of hook flags within Properties: Chord Port of https://github.com/Jojo-Schmitz/MuseScore/pull/742 --- src/engraving/api/v1/elements.h | 1 + src/engraving/dom/chord.cpp | 38 ++++++++++++------- src/engraving/dom/chord.h | 4 ++ src/engraving/dom/engravingitem.cpp | 5 +++ src/engraving/dom/engravingitem.h | 1 + src/engraving/dom/property.cpp | 1 + src/engraving/dom/property.h | 1 + src/engraving/iengravingfont.h | 1 + src/engraving/internal/engravingfont.cpp | 9 +++++ src/engraving/internal/engravingfont.h | 1 + src/engraving/rendering/score/tdraw.cpp | 9 ++++- src/engraving/rendering/score/tlayout.cpp | 2 +- src/engraving/rw/read410/tread.cpp | 2 + src/engraving/rw/write/twrite.cpp | 9 ++++- .../notes/chords/chordsettingsmodel.cpp | 7 ++++ .../notes/chords/chordsettingsmodel.h | 9 +++-- .../Inspector/notation/notes/StemSettings.qml | 9 +++++ 17 files changed, 88 insertions(+), 21 deletions(-) diff --git a/src/engraving/api/v1/elements.h b/src/engraving/api/v1/elements.h index 7e424c4e04319..e1264c81665c5 100644 --- a/src/engraving/api/v1/elements.h +++ b/src/engraving/api/v1/elements.h @@ -188,6 +188,7 @@ class EngravingItem : public apiv1::ScoreElement API_PROPERTY(articulationAnchor, ARTICULATION_ANCHOR) API_PROPERTY(direction, DIRECTION) API_PROPERTY(stemDirection, STEM_DIRECTION) + API_PROPERTY(hookReversed, HOOK_REVERSED) API_PROPERTY(noStem, NO_STEM) API_PROPERTY(slurDirection, SLUR_DIRECTION) API_PROPERTY(leadingSpace, LEADING_SPACE) diff --git a/src/engraving/dom/chord.cpp b/src/engraving/dom/chord.cpp index 87ba563151c9f..bf4a406853d39 100644 --- a/src/engraving/dom/chord.cpp +++ b/src/engraving/dom/chord.cpp @@ -252,6 +252,7 @@ Chord::Chord(Segment* parent) m_stem = 0; m_hook = 0; m_stemDirection = DirectionV::AUTO; + m_hookIsReversed = false; m_arpeggio = 0; m_spanArpeggio = 0; m_endsNoteAnchoredLine = false; @@ -297,15 +298,16 @@ Chord::Chord(const Chord& c, bool link) m_arpeggio = 0; m_stemSlash = 0; - m_spanArpeggio = c.m_spanArpeggio; - m_graceIndex = c.m_graceIndex; - m_noStem = c.m_noStem; - m_showStemSlash = c.m_showStemSlash; - m_playEventType = c.m_playEventType; - m_stemDirection = c.m_stemDirection; - m_noteType = c.m_noteType; - m_crossMeasure = CrossMeasure::UNKNOWN; - m_combineVoice = c.m_combineVoice; + m_spanArpeggio = c.m_spanArpeggio; + m_graceIndex = c.m_graceIndex; + m_noStem = c.m_noStem; + m_showStemSlash = c.m_showStemSlash; + m_playEventType = c.m_playEventType; + m_stemDirection = c.m_stemDirection; + m_hookIsReversed = c.m_hookIsReversed; + m_noteType = c.m_noteType; + m_crossMeasure = CrossMeasure::UNKNOWN; + m_combineVoice = c.m_combineVoice; if (c.m_stem) { add(Factory::copyStem(*(c.m_stem))); @@ -2087,8 +2089,10 @@ PropertyValue Chord::getProperty(Pid propertyId) const case Pid::SHOW_STEM_SLASH: return showStemSlash(); case Pid::SMALL: return isSmall(); case Pid::STEM_DIRECTION: return PropertyValue::fromValue(stemDirection()); - case Pid::PLAY: return isChordPlayable(); - case Pid::COMBINE_VOICE: return PropertyValue::fromValue(combineVoice()); + case Pid::PLAY: return isChordPlayable(); + case Pid::COMBINE_VOICE: return PropertyValue::fromValue(combineVoice()); + case Pid::HOOK_REVERSED: return hookIsReversed(); + default: return ChordRest::getProperty(propertyId); } @@ -2102,11 +2106,13 @@ PropertyValue Chord::propertyDefault(Pid propertyId) const { switch (propertyId) { case Pid::NO_STEM: return false; - case Pid::SHOW_STEM_SLASH: return noteType() == NoteType::ACCIACCATURA; + case Pid::SHOW_STEM_SLASH: return noteType() & NoteType::ACCIACCATURA; case Pid::SMALL: return false; case Pid::STEM_DIRECTION: return PropertyValue::fromValue(DirectionV::AUTO); - case Pid::PLAY: return true; - case Pid::COMBINE_VOICE: return AutoOnOff::AUTO; + case Pid::PLAY: return true; + case Pid::COMBINE_VOICE: return AutoOnOff::AUTO; + case Pid::HOOK_REVERSED: return false; + default: return ChordRest::propertyDefault(propertyId); } @@ -2137,6 +2143,10 @@ bool Chord::setProperty(Pid propertyId, const PropertyValue& v) case Pid::COMBINE_VOICE: setCombineVoice(v.value()); break; + case Pid::HOOK_REVERSED: + setHookReversed(v.toBool()); + break; + default: return ChordRest::setProperty(propertyId, v); } diff --git a/src/engraving/dom/chord.h b/src/engraving/dom/chord.h index 04b0e22d89f32..db163f354c03e 100644 --- a/src/engraving/dom/chord.h +++ b/src/engraving/dom/chord.h @@ -283,6 +283,9 @@ class Chord final : public ChordRest Note* firstGraceOrNote(); + bool hookIsReversed() const { return hook() ? m_hookIsReversed : false; } + void setHookReversed(bool v) { m_hookIsReversed = v; } + #ifndef ENGRAVING_NO_ACCESSIBILITY AccessibleItemPtr createAccessible() override; #endif @@ -354,6 +357,7 @@ class Chord final : public ChordRest Stem* m_stem = nullptr; Hook* m_hook = nullptr; + bool m_hookIsReversed = false; StemSlash* m_stemSlash = nullptr; // for grace notes bool m_showStemSlash = false; diff --git a/src/engraving/dom/engravingitem.cpp b/src/engraving/dom/engravingitem.cpp index ed991eddf1764..ad0d6bd35d6b4 100644 --- a/src/engraving/dom/engravingitem.cpp +++ b/src/engraving/dom/engravingitem.cpp @@ -1725,6 +1725,11 @@ void EngravingItem::undoAddElement(EngravingItem* element, bool addToLinkedStave // drawSymbol //--------------------------------------------------------- +void EngravingItem::drawSymbolReversed(SymId id, Painter* p, const PointF& o, double scale) const +{ + score()->engravingFont()->drawReversed(id, p, magS() * scale, o); +} + void EngravingItem::drawSymbol(SymId id, Painter* p, const PointF& o, double scale) const { score()->engravingFont()->draw(id, p, magS() * scale, o); diff --git a/src/engraving/dom/engravingitem.h b/src/engraving/dom/engravingitem.h index f5c5fc0ed7445..3d6c8c83985f9 100644 --- a/src/engraving/dom/engravingitem.h +++ b/src/engraving/dom/engravingitem.h @@ -442,6 +442,7 @@ class EngravingItem : public EngravingObject bool custom(Pid) const; virtual bool isUserModified() const; + void drawSymbolReversed(SymId id, muse::draw::Painter* p, const PointF& o = PointF(), double scale = 1.0) const; void drawSymbol(SymId id, muse::draw::Painter* p, const PointF& o = PointF(), double scale = 1.0) const; void drawSymbols(const SymIdList&, muse::draw::Painter* p, const PointF& o = PointF(), double scale = 1.0) const; void drawSymbols(const SymIdList&, muse::draw::Painter* p, const PointF& o, const SizeF& scale) const; diff --git a/src/engraving/dom/property.cpp b/src/engraving/dom/property.cpp index 7b8d4119a4318..05e4c7ca2d137 100644 --- a/src/engraving/dom/property.cpp +++ b/src/engraving/dom/property.cpp @@ -85,6 +85,7 @@ static constexpr PropertyMetaData propertyList[] = { { Pid::DIRECTION, false, "direction", P_TYPE::DIRECTION_V, PropertyGroup::POSITION, DUMMY_QT_TR_NOOP("propertyName", "direction") }, { Pid::STEM_DIRECTION, false, "StemDirection", P_TYPE::DIRECTION_V, PropertyGroup::APPEARANCE, DUMMY_QT_TR_NOOP("propertyName", "stem direction") }, { Pid::NO_STEM, false, "noStem", P_TYPE::INT, PropertyGroup::APPEARANCE, DUMMY_QT_TR_NOOP("propertyName", "no stem") }, + { Pid::HOOK_REVERSED, false, "hookReverse", P_TYPE::BOOL, PropertyGroup::POSITION, DUMMY_QT_TR_NOOP("propertyName", "reverse hook") }, { Pid::SLUR_DIRECTION, false, "up", P_TYPE::DIRECTION_V, PropertyGroup::POSITION, DUMMY_QT_TR_NOOP("propertyName", "up") }, { Pid::LEADING_SPACE, false, "leadingSpace", P_TYPE::SPATIUM, PropertyGroup::POSITION, DUMMY_QT_TR_NOOP("propertyName", "leading space") }, { Pid::MIRROR_HEAD, false, "mirror", P_TYPE::DIRECTION_H, PropertyGroup::POSITION, DUMMY_QT_TR_NOOP("propertyName", "mirror") }, diff --git a/src/engraving/dom/property.h b/src/engraving/dom/property.h index fb8615da04019..14654496f3bde 100644 --- a/src/engraving/dom/property.h +++ b/src/engraving/dom/property.h @@ -96,6 +96,7 @@ enum class Pid { DIRECTION, STEM_DIRECTION, NO_STEM, + HOOK_REVERSED, SLUR_DIRECTION, LEADING_SPACE, MIRROR_HEAD, diff --git a/src/engraving/iengravingfont.h b/src/engraving/iengravingfont.h index b9d8c90af5fb5..8c2d9ab1e339f 100644 --- a/src/engraving/iengravingfont.h +++ b/src/engraving/iengravingfont.h @@ -52,6 +52,7 @@ class IEngravingFont virtual PointF smuflAnchor(SymId symId, SmuflAnchorId anchorId, double mag) const = 0; // Draw + virtual void drawReversed(SymId id, muse::draw::Painter* p, double mag, const PointF& pos) const = 0; virtual void draw(SymId id, muse::draw::Painter* p, double mag, const PointF& pos, const double angle = 0) const = 0; virtual void draw(SymId id, muse::draw::Painter* p, const SizeF& mag, const PointF& pos, const double angle = 0) const = 0; virtual void draw(const SymIdList& ids, muse::draw::Painter* p, double mag, const PointF& pos, const double angle = 0) const = 0; diff --git a/src/engraving/internal/engravingfont.cpp b/src/engraving/internal/engravingfont.cpp index 6cf2612af3692..a101c8f7a40d3 100644 --- a/src/engraving/internal/engravingfont.cpp +++ b/src/engraving/internal/engravingfont.cpp @@ -747,6 +747,15 @@ PointF EngravingFont::smuflAnchor(SymId symId, SmuflAnchorId anchorId, double ma // Draw // ============================================= +void EngravingFont::drawReversed(SymId id, Painter* painter, double mag, const PointF& pos) const +{ + double worldScale = painter->worldTransform().m11(); + painter->save(); + painter->scale(-1.0, +1.0); + draw(id, painter, SizeF(mag, mag), pos, worldScale); + painter->restore(); +} + void EngravingFont::draw(SymId id, Painter* painter, const SizeF& mag, const PointF& pos, const double angle) const { const Sym& sym = this->sym(id); diff --git a/src/engraving/internal/engravingfont.h b/src/engraving/internal/engravingfont.h index a6f1d19e509f8..0e5a60e555a5c 100644 --- a/src/engraving/internal/engravingfont.h +++ b/src/engraving/internal/engravingfont.h @@ -87,6 +87,7 @@ class EngravingFont : public IEngravingFont, public muse::Injectable PointF smuflAnchor(SymId symId, SmuflAnchorId anchorId, double mag) const override; // Draw + void drawReversed(SymId id, muse::draw::Painter*, double mag, const PointF& pos) const override; void draw(SymId id, muse::draw::Painter* p, double mag, const PointF& pos, const double angle = 0) const override; void draw(SymId id, muse::draw::Painter* p, const SizeF& mag, const PointF& pos, const double angle = 0) const override; diff --git a/src/engraving/rendering/score/tdraw.cpp b/src/engraving/rendering/score/tdraw.cpp index 435510dcc067d..395a309c52ad8 100644 --- a/src/engraving/rendering/score/tdraw.cpp +++ b/src/engraving/rendering/score/tdraw.cpp @@ -1933,7 +1933,14 @@ void TDraw::draw(const Hook* item, Painter* painter) } painter->setPen(item->curColor()); - item->drawSymbol(item->sym(), painter); + if (item->chord()->hookIsReversed()) { + double scale = 1.0; + double dx = item->chord()->hook()->width() + (1.5 * item->chord()->stem()->lineWidthMag()); + PointF offset = PointF(-dx, 0.0); + item->drawSymbolReversed(item->sym(), painter, offset, scale); + } else { + item->drawSymbol(item->sym(), painter); + } } void TDraw::draw(const Image* item, Painter* painter) diff --git a/src/engraving/rendering/score/tlayout.cpp b/src/engraving/rendering/score/tlayout.cpp index c0d3c3e33de56..708279fcfac25 100644 --- a/src/engraving/rendering/score/tlayout.cpp +++ b/src/engraving/rendering/score/tlayout.cpp @@ -5356,7 +5356,7 @@ void TLayout::layoutStem(const Stem* item, Stem::LayoutData* ldata, const Layout } } - double lineWidthCorrection = item->lineWidthMag() * 0.5; + double lineWidthCorrection = !item->chord()->hookIsReversed() ? item->lineWidthMag() * 0.5 : -item->chord()->hook()->width(); double lineX = isTabStaff ? 0.0 : _up * lineWidthCorrection; LineF line = LineF(lineX, y1, lineX, y2); diff --git a/src/engraving/rw/read410/tread.cpp b/src/engraving/rw/read410/tread.cpp index c62feee89fe35..ffb4b8b07c6c0 100644 --- a/src/engraving/rw/read410/tread.cpp +++ b/src/engraving/rw/read410/tread.cpp @@ -2698,6 +2698,8 @@ bool TRead::readProperties(Chord* ch, XmlReader& e, ReadContext& ctx) TRead::read(ss, e, ctx); ch->add(ss); } else if (TRead::readProperty(ch, tag, e, ctx, Pid::STEM_DIRECTION)) { + } else if (tag == "hookReverse") { + ch->setHookReversed(e.readBool()); } else if (tag == "noStem") { ch->setNoStem(e.readInt()); } else if (tag == "showStemSlash") { diff --git a/src/engraving/rw/write/twrite.cpp b/src/engraving/rw/write/twrite.cpp index d5754b1f0e3c5..9c1bc995b9311 100644 --- a/src/engraving/rw/write/twrite.cpp +++ b/src/engraving/rw/write/twrite.cpp @@ -919,8 +919,13 @@ void TWrite::write(const Chord* item, XmlWriter& xml, WriteContext& ctx) } else if (item->stem() && (item->stem()->isUserModified() || !item->stem()->userLength().isZero())) { write(item->stem(), xml, ctx); } - if (item->hook() && item->hook()->isUserModified()) { - write(item->hook(), xml, ctx); + if (item->hook()) { + if (item->hook()->isUserModified()) { + write(item->hook(), xml, ctx); + } + if (item->hookIsReversed()) { + writeProperty(item, xml, Pid::HOOK_REVERSED); + } } if (item->showStemSlash() && item->isUserModified()) { xml.tag("showStemSlash", item->showStemSlash()); diff --git a/src/inspector/models/notation/notes/chords/chordsettingsmodel.cpp b/src/inspector/models/notation/notes/chords/chordsettingsmodel.cpp index 2b6351912397f..24ea505104276 100644 --- a/src/inspector/models/notation/notes/chords/chordsettingsmodel.cpp +++ b/src/inspector/models/notation/notes/chords/chordsettingsmodel.cpp @@ -55,6 +55,7 @@ void ChordSettingsModel::requestElements() void ChordSettingsModel::loadProperties() { loadPropertyItem(m_isStemless); + loadPropertyItem(m_isHookReversed); loadPropertyItem(m_showStemSlash); loadPropertyItem(m_combineVoice); updateShowStemSlashVisible(); @@ -64,6 +65,7 @@ void ChordSettingsModel::loadProperties() void ChordSettingsModel::resetProperties() { m_isStemless->resetToDefault(); + m_isHookReversed->resetToDefault(); m_showStemSlash->resetToDefault(); updateShowStemSlashEnabled(); } @@ -73,6 +75,11 @@ PropertyItem* ChordSettingsModel::isStemless() const return m_isStemless; } +PropertyItem* ChordSettingsModel::isReversedHook() const +{ + return m_isHookReversed; +} + PropertyItem* ChordSettingsModel::showStemSlash() const { return m_showStemSlash; diff --git a/src/inspector/models/notation/notes/chords/chordsettingsmodel.h b/src/inspector/models/notation/notes/chords/chordsettingsmodel.h index 085aee3eb0688..deed35f5561f8 100644 --- a/src/inspector/models/notation/notes/chords/chordsettingsmodel.h +++ b/src/inspector/models/notation/notes/chords/chordsettingsmodel.h @@ -30,6 +30,7 @@ class ChordSettingsModel : public AbstractInspectorModel Q_OBJECT Q_PROPERTY(PropertyItem * isStemless READ isStemless CONSTANT) + Q_PROPERTY(PropertyItem * isReversedHook READ isReversedHook CONSTANT) Q_PROPERTY(PropertyItem * showStemSlash READ showStemSlash CONSTANT) Q_PROPERTY(PropertyItem * combineVoice READ combineVoice CONSTANT) @@ -40,6 +41,7 @@ class ChordSettingsModel : public AbstractInspectorModel explicit ChordSettingsModel(QObject* parent, IElementRepositoryService* repository); PropertyItem* isStemless() const; + PropertyItem* isReversedHook() const; PropertyItem* showStemSlash() const; PropertyItem* combineVoice() const; @@ -63,9 +65,10 @@ public slots: void updateShowStemSlashVisible(); void updateShowStemSlashEnabled(); - PropertyItem* m_isStemless = nullptr; - PropertyItem* m_showStemSlash = nullptr; - PropertyItem* m_combineVoice = nullptr; + PropertyItem* m_isStemless = nullptr; + PropertyItem* m_isHookReversed = nullptr; + PropertyItem* m_showStemSlash = nullptr; + PropertyItem* m_combineVoice = nullptr; bool m_showStemSlashVisible = false; bool m_showStemSlashEnabled = false; diff --git a/src/inspector/view/qml/MuseScore/Inspector/notation/notes/StemSettings.qml b/src/inspector/view/qml/MuseScore/Inspector/notation/notes/StemSettings.qml index 203259f737bd1..7f35ef3d881fa 100644 --- a/src/inspector/view/qml/MuseScore/Inspector/notation/notes/StemSettings.qml +++ b/src/inspector/view/qml/MuseScore/Inspector/notation/notes/StemSettings.qml @@ -159,6 +159,15 @@ FocusableItem { spacing: 12 + PropertyCheckBox { + text: qsTrc("inspector", "Reversed hook") + propertyItem: root.chordModel ? root.chordModel.isReverseHook : null + + navigationName: "Reversed hook" + navigationPanel: root.navigationPanel + navigationRowStart: root.navigationRowStart + 1 + } + Item { height: childrenRect.height width: parent.width