Added predefined, strongly typed elements for playing techniques

This commit is contained in:
vpereverzev 2022-01-24 17:09:54 +02:00 committed by pereverzev+v
parent 7e3108ccb3
commit a30a668193
31 changed files with 356 additions and 36 deletions

View file

@ -337,7 +337,7 @@ void LayoutMeasure::createMMRest(const LayoutOptions& options, Score* score, Mea
for (EngravingItem* e : underlyingSeg->annotations()) {
// look at elements in underlying measure
if (!(e->isRehearsalMark() || e->isTempoText() || e->isHarmony() || e->isStaffText() || e->isSystemText()
|| e->isInstrumentChange())) {
|| e->isPlayTechAnnotation() || e->isInstrumentChange())) {
continue;
}
// try to find a match in mmr
@ -361,7 +361,7 @@ void LayoutMeasure::createMMRest(const LayoutOptions& options, Score* score, Mea
for (EngravingItem* e : s->annotations()) {
// look at elements in mmr
if (!(e->isRehearsalMark() || e->isTempoText() || e->isHarmony() || e->isStaffText() || e->isSystemText()
|| e->isInstrumentChange())) {
|| e->isPlayTechAnnotation() || e->isInstrumentChange())) {
continue;
}
// try to find a match in underlying measure
@ -400,7 +400,7 @@ static bool validMMRestMeasure(const LayoutContext& ctx, Measure* m)
for (Segment* s = m->first(); s; s = s->next()) {
for (EngravingItem* e : s->annotations()) {
if (!(e->isRehearsalMark() || e->isTempoText() || e->isHarmony() || e->isStaffText() || e->isSystemText()
|| e->isInstrumentChange())) {
|| e->isPlayTechAnnotation() || e->isInstrumentChange())) {
return false;
}
}
@ -505,7 +505,7 @@ static bool breakMultiMeasureRest(const LayoutContext& ctx, Measure* m)
}
if (e->isRehearsalMark()
|| e->isTempoText()
|| ((e->isHarmony() || e->isStaffText() || e->isSystemText() || e->isInstrumentChange())
|| ((e->isHarmony() || e->isStaffText() || e->isSystemText() || e->isPlayTechAnnotation() || e->isInstrumentChange())
&& (e->systemFlag() || ctx.score()->staff(e->staffIdx())->show()))) {
return true;
}

View file

@ -1008,7 +1008,7 @@ void LayoutSystem::layoutSystemElements(const LayoutOptions& options, LayoutCont
for (const Segment* s : sl) {
for (EngravingItem* e : s->annotations()) {
if (e->isStaffText() || e->isSystemText() || e->isInstrumentChange()) {
if (e->isPlayTechAnnotation() || e->isStaffText() || e->isSystemText() || e->isInstrumentChange()) {
e->layout();
}
}

View file

@ -507,6 +507,7 @@ EngravingItem* ChordRest::drop(EditData& data)
case ElementType::TEXT:
case ElementType::STAFF_TEXT:
case ElementType::SYSTEM_TEXT:
case ElementType::PLAYTECH_ANNOTATION:
case ElementType::STICKING:
case ElementType::STAFF_STATE:
// fall through

View file

@ -2138,6 +2138,7 @@ void Score::cmdFlip()
|| e->isJump()
|| e->isMarker()
|| e->isStaffText()
|| e->isPlayTechAnnotation()
|| e->isSticking()
|| e->isFingering()
|| e->isDynamic()
@ -5270,6 +5271,7 @@ void Score::undoAddElement(EngravingItem* element, bool ctrlModifier)
&& et != ElementType::DYNAMIC
&& et != ElementType::STAFF_TEXT
&& et != ElementType::SYSTEM_TEXT
&& et != ElementType::PLAYTECH_ANNOTATION
&& et != ElementType::STICKING
&& et != ElementType::TREMOLO
&& et != ElementType::ARPEGGIO
@ -5330,6 +5332,7 @@ void Score::undoAddElement(EngravingItem* element, bool ctrlModifier)
|| element->isTremoloBar()
|| element->isDynamic()
|| element->isStaffText()
|| element->isPlayTechAnnotation()
|| element->isSticking()
|| element->isFretDiagram()
|| element->isHarmony()
@ -5355,6 +5358,7 @@ void Score::undoAddElement(EngravingItem* element, bool ctrlModifier)
// this should be same list excluded in cloneStaff()
case ElementType::STAFF_TEXT:
case ElementType::SYSTEM_TEXT:
case ElementType::PLAYTECH_ANNOTATION:
case ElementType::FRET_DIAGRAM:
case ElementType::HARMONY:
case ElementType::FIGURED_BASS:
@ -5446,6 +5450,7 @@ void Score::undoAddElement(EngravingItem* element, bool ctrlModifier)
|| element->isTremoloBar()
|| element->isDynamic()
|| element->isStaffText()
|| element->isPlayTechAnnotation()
|| element->isSticking()
|| element->isFretDiagram()
|| element->isFermata()

View file

@ -832,6 +832,7 @@ bool EngravingObject::isTextBase() const
|| type() == ElementType::JUMP
|| type() == ElementType::STAFF_TEXT
|| type() == ElementType::SYSTEM_TEXT
|| type() == ElementType::PLAYTECH_ANNOTATION
|| type() == ElementType::REHEARSAL_MARK
|| type() == ElementType::INSTRUMENT_CHANGE
|| type() == ElementType::FIGURED_BASS

View file

@ -135,6 +135,7 @@ class StaffTypeChange;
class MeasureBase;
class Page;
class SystemText;
class PlayTechAnnotation;
class BracketItem;
class Spanner;
class SpannerSegment;
@ -383,6 +384,7 @@ public:
CONVERT(MMRestRange, MMREST_RANGE)
CONVERT(StaffText, STAFF_TEXT)
CONVERT(SystemText, SYSTEM_TEXT)
CONVERT(PlayTechAnnotation, PLAYTECH_ANNOTATION)
CONVERT(BracketItem, BRACKET_ITEM)
CONVERT(Score, SCORE)
CONVERT(Staff, STAFF)
@ -461,7 +463,7 @@ public:
bool isStaffTextBase() const
{
return isStaffText() || isSystemText();
return isStaffText() || isSystemText() || isPlayTechAnnotation();
}
};
@ -625,6 +627,7 @@ CONVERT(Harmony)
CONVERT(Volta)
CONVERT(Jump)
CONVERT(StaffText)
CONVERT(PlayTechAnnotation)
CONVERT(Ottava)
CONVERT(LayoutBreak)
CONVERT(Segment)

View file

@ -1134,6 +1134,7 @@ void Excerpt::cloneStaff(Staff* srcStaff, Staff* dstStaff)
// this should be same list excluded in Score::undoAddElement()
case ElementType::STAFF_TEXT:
case ElementType::SYSTEM_TEXT:
case ElementType::PLAYTECH_ANNOTATION:
case ElementType::FRET_DIAGRAM:
case ElementType::HARMONY:
case ElementType::FIGURED_BASS:
@ -1378,6 +1379,7 @@ void Excerpt::cloneStaff2(Staff* srcStaff, Staff* dstStaff, const Fraction& star
// this should be same list excluded in Score::undoAddElement()
case ElementType::STAFF_TEXT:
case ElementType::SYSTEM_TEXT:
case ElementType::PLAYTECH_ANNOTATION:
case ElementType::FRET_DIAGRAM:
case ElementType::HARMONY:
case ElementType::FIGURED_BASS:

View file

@ -60,6 +60,7 @@
#include "instrchange.h"
#include "stafftext.h"
#include "systemtext.h"
#include "playtechannotation.h"
#include "rehearsalmark.h"
#include "stafftypechange.h"
#include "tremolo.h"
@ -148,6 +149,7 @@ static const ElementName elementNames[] = {
{ ElementType::TEMPO_TEXT, "Tempo", QT_TRANSLATE_NOOP("elementName", "Tempo") },
{ ElementType::STAFF_TEXT, "StaffText", QT_TRANSLATE_NOOP("elementName", "Staff text") },
{ ElementType::SYSTEM_TEXT, "SystemText", QT_TRANSLATE_NOOP("elementName", "System text") },
{ ElementType::PLAYTECH_ANNOTATION, "PlayTechAnnotation", QT_TRANSLATE_NOOP("elementName", "Playing technique annotation") },
{ ElementType::REHEARSAL_MARK, "RehearsalMark", QT_TRANSLATE_NOOP("elementName", "Rehearsal mark") },
{ ElementType::INSTRUMENT_CHANGE, "InstrumentChange", QT_TRANSLATE_NOOP("elementName", "Instrument change") },
{ ElementType::STAFFTYPE_CHANGE, "StaffTypeChange", QT_TRANSLATE_NOOP("elementName", "Staff type change") },
@ -259,6 +261,7 @@ EngravingItem* Factory::doCreateItem(ElementType type, EngravingItem* parent)
case ElementType::MMREST_RANGE: return new MMRestRange(parent->isMeasure() ? toMeasure(parent) : dummy->measure());
case ElementType::INSTRUMENT_NAME: return new InstrumentName(parent->isSystem() ? toSystem(parent) : dummy->system());
case ElementType::STAFF_TEXT: return new StaffText(parent->isSegment() ? toSegment(parent) : dummy->segment());
case ElementType::PLAYTECH_ANNOTATION: return new PlayTechAnnotation(parent->isSegment() ? toSegment(parent) : dummy->segment());
case ElementType::SYSTEM_TEXT: return new SystemText(parent->isSegment() ? toSegment(parent) : dummy->segment());
case ElementType::REHEARSAL_MARK: return new RehearsalMark(parent->isSegment() ? toSegment(parent) : dummy->segment());
case ElementType::INSTRUMENT_CHANGE: return new InstrumentChange(parent);

View file

@ -339,4 +339,6 @@ set(LIBMSCORE_SRC
${CMAKE_CURRENT_LIST_DIR}/vibrato.h
${CMAKE_CURRENT_LIST_DIR}/volta.cpp
${CMAKE_CURRENT_LIST_DIR}/volta.h
${CMAKE_CURRENT_LIST_DIR}/playtechannotation.cpp
${CMAKE_CURRENT_LIST_DIR}/playtechannotation.h
)

View file

@ -1717,6 +1717,7 @@ bool Note::acceptDrop(EditData& data) const
|| (type == ElementType::TIMESIG)
|| (type == ElementType::BAR_LINE)
|| (type == ElementType::STAFF_TEXT)
|| (type == ElementType::PLAYTECH_ANNOTATION)
|| (type == ElementType::SYSTEM_TEXT)
|| (type == ElementType::STICKING)
|| (type == ElementType::TEMPO_TEXT)

View file

@ -392,6 +392,7 @@ bool Score::pasteStaff(XmlReader& e, Segment* dst, int dstStaff, Fraction scale)
|| tag == "Image"
|| tag == "Text"
|| tag == "StaffText"
|| tag == "PlayTechAnnotation"
|| tag == "TempoText"
|| tag == "FiguredBass"
|| tag == "Sticking"
@ -909,7 +910,7 @@ void Score::pasteSymbols(XmlReader& e, ChordRest* dst)
} else {
undoAddElement(el);
}
} else if (tag == "StaffText" || tag == "Sticking") {
} else if (tag == "StaffText" || tag == "PlayTechAnnotation" || tag == "Sticking") {
EngravingItem* el = Factory::createItemByName(tag, this->dummy());
el->read(e);
el->setTrack(destTrack);

View file

@ -0,0 +1,120 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
#include "playtechannotation.h"
#include "rw/xml.h"
#include "types/typesconv.h"
#include "segment.h"
using namespace mu::engraving;
using namespace Ms;
static const ElementStyle annotationStyle {
{ Sid::staffTextPlacement, Pid::PLACEMENT },
{ Sid::staffTextMinDistance, Pid::MIN_DISTANCE },
};
PlayTechAnnotation::PlayTechAnnotation(Segment* parent, PlayingTechniqueType techniqueType, TextStyleType tid)
: StaffTextBase(ElementType::PLAYTECH_ANNOTATION, parent, tid, ElementFlag::MOVABLE | ElementFlag::ON_STAFF),
m_techniqueType(techniqueType)
{
initElementStyle(&annotationStyle);
}
void PlayTechAnnotation::setTechniqueType(const PlayingTechniqueType techniqueType)
{
m_techniqueType = techniqueType;
}
PlayTechAnnotation* PlayTechAnnotation::clone() const
{
return new PlayTechAnnotation(*this);
}
void PlayTechAnnotation::layout()
{
StaffTextBase::layout();
autoplaceSegmentElement();
}
void PlayTechAnnotation::write(XmlWriter& writer) const
{
writer.startObject(this);
writeProperty(writer, Pid::PLAY_TECH_TYPE);
StaffTextBase::writeProperties(writer);
writer.endObject();
}
void PlayTechAnnotation::read(XmlReader& reader)
{
while (reader.readNextStartElement()) {
const QStringRef& tag(reader.name());
if (readProperty(tag, reader, Pid::PLAY_TECH_TYPE)) {
continue;
}
if (!StaffTextBase::readProperties(reader)) {
reader.unknown();
}
}
}
PropertyValue PlayTechAnnotation::getProperty(Pid id) const
{
switch (id) {
case Pid::PLAY_TECH_TYPE:
return m_techniqueType;
default:
return StaffTextBase::getProperty(id);
}
}
bool PlayTechAnnotation::setProperty(Pid propertyId, const mu::engraving::PropertyValue& val)
{
switch (propertyId) {
case Pid::PLAY_TECH_TYPE:
m_techniqueType = PlayingTechniqueType(val.toInt());
break;
default:
if (!StaffTextBase::setProperty(propertyId, val)) {
return false;
}
break;
}
triggerLayout();
return true;
}
PropertyValue PlayTechAnnotation::propertyDefault(Pid id) const
{
switch (id) {
case Pid::TEXT_STYLE:
return TextStyleType::STAFF;
case Pid::PLAY_TECH_TYPE:
return PlayingTechniqueType::Natural;
default:
return StaffTextBase::propertyDefault(id);
}
}

View file

@ -0,0 +1,54 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
#ifndef MU_ENGRAVING_PLAYINGTECHNIQUEANNOTATION_H
#define MU_ENGRAVING_PLAYINGTECHNIQUEANNOTATION_H
#include "stafftextbase.h"
#include "types/types.h"
namespace Ms {
class PlayTechAnnotation final : public StaffTextBase
{
public:
PlayTechAnnotation(Segment* parent = nullptr, PlayingTechniqueType techniqueType = PlayingTechniqueType::Natural,
TextStyleType tid = TextStyleType::STAFF);
PlayingTechniqueType techniqueType() const;
void setTechniqueType(const PlayingTechniqueType techniqueType);
PlayTechAnnotation* clone() const override;
void layout() override;
private:
void write(XmlWriter& writer) const override;
void read(XmlReader& reader) override;
mu::engraving::PropertyValue getProperty(Pid id) const override;
bool setProperty(Pid propertyId, const mu::engraving::PropertyValue& val) override;
mu::engraving::PropertyValue propertyDefault(Pid id) const override;
PlayingTechniqueType m_techniqueType = PlayingTechniqueType::Undefined;
};
}
#endif // MU_ENGRAVING_PLAYINGTECHNIQUEANNOTATION_H

View file

@ -383,10 +383,12 @@ static constexpr PropertyMetaData propertyList[] = {
{ Pid::START_WITH_MEASURE_ONE, true, "startWithMeasureOne", P_TYPE::BOOL, DUMMY_QT_TR_NOOP("propertyName", "start with measure one") },
{ Pid::FIRST_SYSTEM_INDENTATION,true, "firstSystemIndentation",P_TYPE::BOOL, DUMMY_QT_TR_NOOP("propertyName", "first system indentation") },
{ Pid::PATH, false, "path", P_TYPE::DRAW_PATH, DUMMY_QT_TR_NOOP("propertyName", "path") },
{ Pid::PATH, false, "path", P_TYPE::DRAW_PATH, DUMMY_QT_TR_NOOP("propertyName", "path") },
{ Pid::PREFER_SHARP_FLAT, true, "preferSharpFlat", P_TYPE::INT, DUMMY_QT_TR_NOOP("propertyName", "prefer sharps or flats") },
{ Pid::PLAY_TECH_TYPE, true, "playTechType", P_TYPE::PLAYTECH_TYPE, DUMMY_QT_TR_NOOP("propertyName", "playing technique type") },
{ Pid::END, false, "++end++", P_TYPE::INT, DUMMY_QT_TR_NOOP("propertyName", "<invalid property>") }
};
/* *INDENT-ON* */
@ -569,6 +571,9 @@ PropertyValue readProperty(Pid id, XmlReader& e)
case P_TYPE::DURATION_TYPE_WITH_DOTS:
case P_TYPE::INT_LIST:
return PropertyValue();
case P_TYPE::PLAYTECH_TYPE:
return PropertyValue(TConv::fromXml(e.readElementText(), PlayingTechniqueType::Natural));
default:
qFatal("unhandled PID type");
break;

View file

@ -388,6 +388,7 @@ enum class Pid {
PATH, // for ChordLine to make its shape changes undoable
PREFER_SHARP_FLAT,
PLAY_TECH_TYPE,
END
};

View file

@ -186,6 +186,7 @@ bool Rest::acceptDrop(EditData& data) const
|| (type == ElementType::TIMESIG)
|| (type == ElementType::SYSTEM_TEXT)
|| (type == ElementType::STAFF_TEXT)
|| (type == ElementType::PLAYTECH_ANNOTATION)
|| (type == ElementType::BAR_LINE)
|| (type == ElementType::BREATH)
|| (type == ElementType::CHORD)

View file

@ -565,6 +565,7 @@ void Score::writeSegments(XmlWriter& xml, int strack, int etrack,
ElementType et = e1->type();
if ((et == ElementType::REHEARSAL_MARK)
|| (et == ElementType::SYSTEM_TEXT)
|| (et == ElementType::PLAYTECH_ANNOTATION)
|| (et == ElementType::JUMP)
|| (et == ElementType::MARKER)
|| (et == ElementType::TEMPO_TEXT)

View file

@ -576,6 +576,7 @@ void Segment::add(EngravingItem* el)
case ElementType::FRET_DIAGRAM:
case ElementType::STAFF_TEXT:
case ElementType::SYSTEM_TEXT:
case ElementType::PLAYTECH_ANNOTATION:
case ElementType::REHEARSAL_MARK:
case ElementType::MARKER:
case ElementType::IMAGE:
@ -740,6 +741,7 @@ void Segment::remove(EngravingItem* el)
case ElementType::REHEARSAL_MARK:
case ElementType::STAFF_TEXT:
case ElementType::SYSTEM_TEXT:
case ElementType::PLAYTECH_ANNOTATION:
case ElementType::SYMBOL:
case ElementType::TAB_DURATION_SYMBOL:
case ElementType::TEMPO_TEXT:
@ -860,6 +862,7 @@ void Segment::sortStaves(QList<int>& dst)
if (!e->systemFlag()
|| (et == ElementType::REHEARSAL_MARK)
|| (et == ElementType::SYSTEM_TEXT)
|| (et == ElementType::PLAYTECH_ANNOTATION)
|| (et == ElementType::JUMP)
|| (et == ElementType::MARKER)
|| (et == ElementType::TEMPO_TEXT)
@ -1757,6 +1760,7 @@ EngravingItem* Segment::nextElement(int activeStaff)
case ElementType::TEMPO_TEXT:
case ElementType::STAFF_TEXT:
case ElementType::SYSTEM_TEXT:
case ElementType::PLAYTECH_ANNOTATION:
case ElementType::REHEARSAL_MARK:
case ElementType::MARKER:
case ElementType::IMAGE:
@ -1898,6 +1902,7 @@ EngravingItem* Segment::prevElement(int activeStaff)
case ElementType::TEMPO_TEXT:
case ElementType::STAFF_TEXT:
case ElementType::SYSTEM_TEXT:
case ElementType::PLAYTECH_ANNOTATION:
case ElementType::REHEARSAL_MARK:
case ElementType::MARKER:
case ElementType::IMAGE:
@ -2264,7 +2269,8 @@ void Segment::createShape(int staffIdx)
&& !e->isInstrumentChange()
&& !e->isArticulation()
&& !e->isFermata()
&& !e->isStaffText()) {
&& !e->isStaffText()
&& !e->isPlayTechAnnotation()) {
// annotations added here are candidates for collision detection
// lyrics, ...
s.add(e->shape().translated(e->pos()));

View file

@ -70,6 +70,7 @@
#include "stafftext.h"
#include "sticking.h"
#include "linkedobjects.h"
#include "playtechannotation.h"
#include "log.h"
@ -1003,8 +1004,9 @@ QByteArray Selection::symbolListMimeData() const
break;
}
continue;
case ElementType::PLAYTECH_ANNOTATION:
case ElementType::STAFF_TEXT:
seg = toStaffText(e)->segment();
seg = toStaffTextBase(e)->segment();
break;
case ElementType::STICKING:
seg = toSticking(e)->segment();

View file

@ -91,6 +91,7 @@ enum class ElementType {
TEMPO_TEXT,
STAFF_TEXT,
SYSTEM_TEXT,
PLAYTECH_ANNOTATION,
REHEARSAL_MARK,
INSTRUMENT_CHANGE,
STAFFTYPE_CHANGE,

View file

@ -152,6 +152,7 @@ QVariant PropertyValue::toQVariant() const
case P_TYPE::HOOK_TYPE: return static_cast<int>(value<HookType>());
case P_TYPE::KEY_MODE: return static_cast<int>(value<KeyMode>());
case P_TYPE::TEXT_STYLE: return static_cast<int>(value<TextStyleType>());
case P_TYPE::PLAYTECH_TYPE: return static_cast<int>(value<PlayingTechniqueType>());
// Other
case P_TYPE::GROUPS: {
@ -238,6 +239,7 @@ PropertyValue PropertyValue::fromQVariant(const QVariant& v, P_TYPE type)
case P_TYPE::HOOK_TYPE: return PropertyValue(HookType(v.toInt()));
case P_TYPE::KEY_MODE: return PropertyValue(KeyMode(v.toInt()));
case P_TYPE::TEXT_STYLE: return PropertyValue(TextStyleType(v.toInt()));
case P_TYPE::PLAYTECH_TYPE: return PropertyValue(PlayingTechniqueType(v.toInt()));
// Other
case P_TYPE::GROUPS: {

View file

@ -96,6 +96,7 @@ enum class P_TYPE {
HOOK_TYPE,
KEY_MODE,
TEXT_STYLE,
PLAYTECH_TYPE,
// Other
GROUPS,
@ -234,6 +235,9 @@ public:
PropertyValue(TextStyleType v)
: m_type(P_TYPE::TEXT_STYLE), m_data(make_data<TextStyleType>(v)) {}
PropertyValue(PlayingTechniqueType v)
: m_type(P_TYPE::PLAYTECH_TYPE), m_data(make_data<PlayingTechniqueType>(v)) {}
// Other
PropertyValue(const GroupNodes& v)
: m_type(P_TYPE::GROUPS), m_data(make_data<GroupNodes>(v)) {}

View file

@ -436,6 +436,7 @@ void MeasureRW::readVoice(Measure* measure, XmlReader& e, ReadContext& ctx, int
|| tag == "StaffText"
|| tag == "Sticking"
|| tag == "SystemText"
|| tag == "PlayTechAnnotation"
|| tag == "RehearsalMark"
|| tag == "InstrumentChange"
|| tag == "StaffState"
@ -610,6 +611,7 @@ void MeasureRW::writeMeasure(const Ms::Measure* measure, XmlWriter& xml, int sta
ElementType et = e->type();
if ((et == ElementType::REHEARSAL_MARK)
|| (et == ElementType::SYSTEM_TEXT)
|| (et == ElementType::PLAYTECH_ANNOTATION)
|| (et == ElementType::JUMP)
|| (et == ElementType::MARKER)
|| (et == ElementType::TEMPO_TEXT)

View file

@ -445,6 +445,12 @@ void XmlWriter::tagProperty(const char* name, P_TYPE type, const PropertyValue&
*this << TConv::toXml(data.value<AccidentalRole>());
*this << "</" << ename << ">\n";
} break;
case P_TYPE::PLAYTECH_TYPE: {
putLevel();
*this << "<" << name << ">";
*this << TConv::toXml(data.value<PlayingTechniqueType>());
*this << "</" << ename << ">\n";
} break;
default: {
UNREACHABLE; //! TODO
}

View file

@ -489,6 +489,44 @@ enum class TextStyleType {
IGNORED_TYPES // used for types no longer relevant (mainly Figured bass text type)
};
enum class AnnotationCategory {
Undefined = -1,
TempoAnnotation,
PlayingAnnotation,
Other,
};
enum class PlayingTechniqueType {
Undefined = -1,
Natural,
Pizzicato,
Open,
Mute,
Tremolo,
Detache,
Martele,
ColLegno,
SulPonticello,
SulTasto,
Vibrato,
Legato
};
enum class TempoTechniqueType {
Undefined = -1,
Accelerando,
Allargando,
Calando,
Lentando,
Morendo,
Precipitando,
Rallentando,
Ritardando,
Smorzando,
Sostenuto,
Stringendo
};
// P_TYPE::CHANGE_METHOD
enum class ChangeMethod : signed char {
NORMAL,
@ -542,6 +580,8 @@ using ChangeDirection = mu::engraving::ChangeDirection;
using AccidentalRole = mu::engraving::AccidentalRole;
using DurationType = mu::engraving::DurationType;
using DurationTypeWithDots = mu::engraving::DurationTypeWithDots;
using PlayingTechniqueType = mu::engraving::PlayingTechniqueType;
using TempoTechniqueType = mu::engraving::TempoTechniqueType;
}
#endif // MU_ENGRAVING_TYPES_H

View file

@ -807,3 +807,29 @@ DurationType TConv::fromXml(const QString& tag, DurationType def)
{
return findTypeByXmlTag<DurationType>(DURATION_TYPES, tag, def);
}
static const std::vector<Item<PlayingTechniqueType> > PLAY_TECH_TYPES = {
{ PlayingTechniqueType::Undefined, "undefined" },
{ PlayingTechniqueType::Natural, "natural" },
{ PlayingTechniqueType::Pizzicato, "pizzicato" },
{ PlayingTechniqueType::Open, "open" },
{ PlayingTechniqueType::Mute, "mute" },
{ PlayingTechniqueType::Tremolo, "tremolo" },
{ PlayingTechniqueType::Detache, "detache" },
{ PlayingTechniqueType::Martele, "martele" },
{ PlayingTechniqueType::ColLegno, "col_legno" },
{ PlayingTechniqueType::SulPonticello, "sul_ponticello" },
{ PlayingTechniqueType::SulTasto, "sul_tasto" },
{ PlayingTechniqueType::Vibrato, "vibrato" },
{ PlayingTechniqueType::Legato, "legato" }
};
QString TConv::toXml(PlayingTechniqueType v)
{
return findXmlTagByType<PlayingTechniqueType>(PLAY_TECH_TYPES, v);
}
PlayingTechniqueType TConv::fromXml(const QString& tag, PlayingTechniqueType def)
{
return findTypeByXmlTag<PlayingTechniqueType>(PLAY_TECH_TYPES, tag, def);
}

View file

@ -100,6 +100,9 @@ public:
static QString toUserName(DurationType v);
static QString toXml(DurationType v);
static DurationType fromXml(const QString& tag, DurationType def);
static QString toXml(PlayingTechniqueType v);
static PlayingTechniqueType fromXml(const QString& tag, PlayingTechniqueType def);
};
}

View file

@ -60,6 +60,7 @@ TEST_F(ElementTests, DISABLED_testIds)
ElementType::TEXT,
ElementType::INSTRUMENT_NAME,
ElementType::STAFF_TEXT,
ElementType::PLAYTECH_ANNOTATION,
ElementType::REHEARSAL_MARK,
ElementType::INSTRUMENT_CHANGE,
ElementType::NOTEHEAD,

View file

@ -3978,6 +3978,7 @@ static void directionTag(XmlWriter& xml, Attributes& attr, EngravingItem const*
|| el->type() == ElementType::INSTRUMENT_CHANGE
|| el->type() == ElementType::REHEARSAL_MARK
|| el->type() == ElementType::STAFF_TEXT
|| el->type() == ElementType::PLAYTECH_ANNOTATION
|| el->type() == ElementType::SYMBOL
|| el->type() == ElementType::TEXT) {
// handle other elements attached (e.g. via Segment / Measure) to a system
@ -5562,7 +5563,8 @@ static bool commonAnnotations(ExportMusicXml* exp, const EngravingItem* e, int s
exp->symbol(toSymbol(e), sstaff);
} else if (e->isTempoText()) {
exp->tempoText(toTempoText(e), sstaff);
} else if (e->isStaffText() || e->isSystemText() || e->isText() || (e->isInstrumentChange() && e->visible())) {
} else if (e->isPlayTechAnnotation() || e->isStaffText() || e->isSystemText() || e->isText()
|| (e->isInstrumentChange() && e->visible())) {
exp->words(toTextBase(e), sstaff);
} else if (e->isDynamic()) {
exp->dynamic(toDynamic(e), sstaff);

View file

@ -983,6 +983,7 @@ bool NotationInteraction::isDropAccepted(const PointF& pos, Qt::KeyboardModifier
case ElementType::TEMPO_TEXT:
case ElementType::STAFF_TEXT:
case ElementType::SYSTEM_TEXT:
case ElementType::PLAYTECH_ANNOTATION:
case ElementType::NOTEHEAD:
case ElementType::TREMOLO:
case ElementType::LAYOUT_BREAK:
@ -1136,6 +1137,7 @@ bool NotationInteraction::drop(const PointF& pos, Qt::KeyboardModifiers modifier
case ElementType::TEMPO_TEXT:
case ElementType::STAFF_TEXT:
case ElementType::SYSTEM_TEXT:
case ElementType::PLAYTECH_ANNOTATION:
case ElementType::NOTEHEAD:
case ElementType::TREMOLO:
case ElementType::LAYOUT_BREAK:

View file

@ -71,6 +71,7 @@
#include "libmscore/spacer.h"
#include "libmscore/staffstate.h"
#include "libmscore/stafftext.h"
#include "libmscore/playtechannotation.h"
#include "libmscore/stafftypechange.h"
#include "libmscore/systemtext.h"
#include "libmscore/tempo.h"
@ -103,6 +104,7 @@ MAKE_ELEMENT(Hairpin, score->dummy()->segment())
MAKE_ELEMENT(SystemText, score->dummy()->segment())
MAKE_ELEMENT(TempoText, score->dummy()->segment())
MAKE_ELEMENT(StaffText, score->dummy()->segment())
MAKE_ELEMENT(PlayTechAnnotation, score->dummy()->segment())
MAKE_ELEMENT(RehearsalMark, score->dummy()->segment())
MAKE_ELEMENT(Jump, score->dummy()->measure())
@ -1293,48 +1295,68 @@ PalettePtr PaletteCreator::newTextPalette(bool defaultPalette)
sp->appendElement(meaNum, QT_TRANSLATE_NOOP("palette", "Measure number"))->setElementTranslated(true);
if (!defaultPalette) {
auto pz = makeElement<StaffText>(gpaletteScore);
auto pz = makeElement<PlayTechAnnotation>(gpaletteScore);
pz->setXmlText(QT_TRANSLATE_NOOP("palette", "pizz."));
pz->setChannelName(0, "pizzicato");
pz->setChannelName(1, "pizzicato");
pz->setChannelName(2, "pizzicato");
pz->setChannelName(3, "pizzicato");
pz->setTechniqueType(PlayingTechniqueType::Pizzicato);
sp->appendElement(pz, QT_TRANSLATE_NOOP("palette", "Pizzicato"))->setElementTranslated(true);
auto ar = makeElement<StaffText>(gpaletteScore);
auto ar = makeElement<PlayTechAnnotation>(gpaletteScore);
ar->setXmlText(QT_TRANSLATE_NOOP("palette", "arco"));
ar->setChannelName(0, "arco");
ar->setChannelName(1, "arco");
ar->setChannelName(2, "arco");
ar->setChannelName(3, "arco");
ar->setTechniqueType(PlayingTechniqueType::Natural);
sp->appendElement(ar, QT_TRANSLATE_NOOP("palette", "Arco"))->setElementTranslated(true);
auto tm = makeElement<StaffText>(gpaletteScore);
auto detache = makeElement<PlayTechAnnotation>(gpaletteScore);
detache->setXmlText(QT_TRANSLATE_NOOP("palette", "detache"));
detache->setTechniqueType(PlayingTechniqueType::Detache);
sp->appendElement(detache, QT_TRANSLATE_NOOP("palette", "Detache"))->setElementTranslated(true);
auto martele = makeElement<PlayTechAnnotation>(gpaletteScore);
martele->setXmlText(QT_TRANSLATE_NOOP("palette", "martele"));
martele->setTechniqueType(PlayingTechniqueType::Martele);
sp->appendElement(martele, QT_TRANSLATE_NOOP("palette", "Martele"))->setElementTranslated(true);
auto colLegno = makeElement<PlayTechAnnotation>(gpaletteScore);
colLegno->setXmlText(QT_TRANSLATE_NOOP("palette", "col legno"));
colLegno->setTechniqueType(PlayingTechniqueType::ColLegno);
sp->appendElement(colLegno, QT_TRANSLATE_NOOP("palette", "Martele"))->setElementTranslated(true);
auto sulPont = makeElement<PlayTechAnnotation>(gpaletteScore);
sulPont->setXmlText(QT_TRANSLATE_NOOP("palette", "sul pont."));
sulPont->setTechniqueType(PlayingTechniqueType::SulPonticello);
sp->appendElement(sulPont, QT_TRANSLATE_NOOP("palette", "Sul Ponticello"))->setElementTranslated(true);
auto sulTasto = makeElement<PlayTechAnnotation>(gpaletteScore);
sulTasto->setXmlText(QT_TRANSLATE_NOOP("palette", "sul tasto"));
sulTasto->setTechniqueType(PlayingTechniqueType::SulTasto);
sp->appendElement(sulTasto, QT_TRANSLATE_NOOP("palette", "Sul Tasto"))->setElementTranslated(true);
auto vibrato = makeElement<PlayTechAnnotation>(gpaletteScore);
vibrato->setXmlText(QT_TRANSLATE_NOOP("palette", "vibrato"));
vibrato->setTechniqueType(PlayingTechniqueType::Vibrato);
sp->appendElement(vibrato, QT_TRANSLATE_NOOP("palette", "Vibrato"))->setElementTranslated(true);
auto legato = makeElement<PlayTechAnnotation>(gpaletteScore);
legato->setXmlText(QT_TRANSLATE_NOOP("palette", "legato"));
legato->setTechniqueType(PlayingTechniqueType::Legato);
sp->appendElement(legato, QT_TRANSLATE_NOOP("palette", "Legato"))->setElementTranslated(true);
auto tm = makeElement<PlayTechAnnotation>(gpaletteScore);
tm->setTextStyleType(TextStyleType::EXPRESSION);
tm->setXmlText(QT_TRANSLATE_NOOP("palette", "tremolo"));
tm->setChannelName(0, "tremolo");
tm->setChannelName(1, "tremolo");
tm->setChannelName(2, "tremolo");
tm->setChannelName(3, "tremolo");
tm->setTechniqueType(PlayingTechniqueType::Tremolo);
sp->appendElement(tm, QT_TRANSLATE_NOOP("palette", "Tremolo"))->setElementTranslated(true);
auto mu = makeElement<StaffText>(gpaletteScore);
auto mu = makeElement<PlayTechAnnotation>(gpaletteScore);
/*: For brass and plucked string instruments: staff text that prescribes to use mute while playing, see https://en.wikipedia.org/wiki/Mute_(music) */
mu->setXmlText(QT_TRANSLATE_NOOP("palette", "mute"));
mu->setChannelName(0, "mute");
mu->setChannelName(1, "mute");
mu->setChannelName(2, "mute");
mu->setChannelName(3, "mute");
mu->setTechniqueType(PlayingTechniqueType::Mute);
/*: For brass and plucked string instruments: staff text that prescribes to use mute while playing, see https://en.wikipedia.org/wiki/Mute_(music) */
sp->appendElement(mu, QT_TRANSLATE_NOOP("palette", "Mute"))->setElementTranslated(true);
auto no = makeElement<StaffText>(gpaletteScore);
auto no = makeElement<PlayTechAnnotation>(gpaletteScore);
/*: For brass and plucked string instruments: staff text that prescribes to play without mute, see https://en.wikipedia.org/wiki/Mute_(music) */
no->setXmlText(QT_TRANSLATE_NOOP("palette", "open"));
no->setChannelName(0, "open");
no->setChannelName(1, "open");
no->setChannelName(2, "open");
no->setChannelName(3, "open");
no->setTechniqueType(PlayingTechniqueType::Open);
/*: For brass and plucked string instruments: staff text that prescribes to play without mute, see https://en.wikipedia.org/wiki/Mute_(music) */
sp->appendElement(no, QT_TRANSLATE_NOOP("palette", "Open"))->setElementTranslated(true);