From 05b948c0ff7d3a9a4c91a6095dfc2298a6555a14 Mon Sep 17 00:00:00 2001 From: Eism Date: Fri, 6 Oct 2023 16:46:14 +0200 Subject: [PATCH] Fixed testing issues --- src/engraving/dom/measure.cpp | 83 +++++++++++++++---- src/engraving/dom/measure.h | 4 + src/engraving/dom/note.cpp | 14 ++++ src/engraving/dom/rest.cpp | 13 +++ src/engraving/dom/stringtunings.cpp | 2 +- src/engraving/rendering/dev/tlayout.cpp | 14 ++-- src/engraving/rendering/dev/tlayout.h | 2 +- src/engraving/rendering/stable/tlayout.cpp | 55 +++++++++++- src/engraving/rendering/stable/tlayout.h | 2 +- .../internal/stringtuningssettingsmodel.cpp | 66 ++++++++++----- .../internal/stringtuningssettingsmodel.h | 2 + 11 files changed, 210 insertions(+), 47 deletions(-) diff --git a/src/engraving/dom/measure.cpp b/src/engraving/dom/measure.cpp index 46101b6da8138..7e74df2f13ec7 100644 --- a/src/engraving/dom/measure.cpp +++ b/src/engraving/dom/measure.cpp @@ -1333,23 +1333,7 @@ bool Measure::acceptDrop(EditData& data) const return true; case ElementType::STRING_TUNINGS: { - const StringData* stringData = score()->staff(staffIdx)->part()->instrument(tick())->stringData(); - if (!stringData || stringData->frettedStrings() == 0) { - return false; - } - - // already a string tunings element in this measure - bool alreadyHasStringTunings = false; - for (const Segment& segment : m_segments) { - for (EngravingItem* element : segment.annotations()) { - if (element && element->isStringTunings()) { - alreadyHasStringTunings = true; - break; - } - } - } - - if (alreadyHasStringTunings) { + if (!canAddStringTunings(staffIdx)) { return false; } @@ -3211,6 +3195,71 @@ void Measure::checkTrailer() } } +void Measure::spaceRightAlignedSegments() +{ + // Collect all the right-aligned segments starting from the back + std::vector rightAlignedSegments; + for (Segment* segment = m_segments.last(); segment; segment = segment->prev()) { + if (segment->enabled() && segment->isRightAligned()) { + rightAlignedSegments.push_back(segment); + } + } + // Compute spacing + static constexpr double arbitraryLowReal = -10000.0; + for (Segment* raSegment : rightAlignedSegments) { + // 1) right-align the segment against the following ones + double minDistAfter = arbitraryLowReal; + for (Segment* seg = raSegment->next(); seg; seg = seg->next()) { + double xDiff = seg->x() - raSegment->x(); + double minDist = raSegment->minHorizontalCollidingDistance(seg); + minDistAfter = std::max(minDistAfter, minDist - xDiff); + } + if (minDistAfter != arbitraryLowReal && raSegment->prevActive()) { + Segment* prevSegment = raSegment->prev(); + prevSegment->setWidth(prevSegment->width() - minDistAfter); + prevSegment->setWidthOffset(prevSegment->widthOffset() - minDistAfter); + raSegment->mutldata()->moveX(-minDistAfter); + raSegment->setWidth(raSegment->width() + minDistAfter); + } + // 2) Make sure the segment isn't colliding with anything behind + double minDistBefore = 0.0; + for (Segment* seg = raSegment->prevActive(); seg; seg = seg->prevActive()) { + double xDiff = raSegment->x() - seg->x(); + double minDist = seg->minHorizontalCollidingDistance(raSegment); + minDistBefore = std::max(minDistBefore, minDist - xDiff); + } + Segment* prevSegment = raSegment->prevActive(); + if (prevSegment) { + prevSegment->setWidth(prevSegment->width() + minDistBefore); + } + for (Segment* seg = raSegment; seg; seg = seg->next()) { + seg->mutldata()->moveX(minDistBefore); + } + setWidth(width() + minDistBefore); + } +} + +bool Measure::canAddStringTunings(staff_idx_t staffIdx) const +{ + const StringData* stringData = score()->staff(staffIdx)->part()->instrument(tick())->stringData(); + if (!stringData || stringData->frettedStrings() == 0) { + return false; + } + + // already a string tunings element in this measure + bool alreadyHasStringTunings = false; + for (const Segment& segment : m_segments) { + for (EngravingItem* element : segment.annotations()) { + if (element && element->isStringTunings()) { + alreadyHasStringTunings = true; + break; + } + } + } + + return !alreadyHasStringTunings; +} + void Measure::stretchToTargetWidth(double targetWidth) { if (targetWidth < width()) { diff --git a/src/engraving/dom/measure.h b/src/engraving/dom/measure.h index 945dcc27c3057..8bf8785a572c9 100644 --- a/src/engraving/dom/measure.h +++ b/src/engraving/dom/measure.h @@ -352,6 +352,10 @@ class Measure final : public MeasureBase void respaceSegments(); + void spaceRightAlignedSegments(); + + bool canAddStringTunings(staff_idx_t staffIdx) const; + private: friend class Factory; diff --git a/src/engraving/dom/note.cpp b/src/engraving/dom/note.cpp index ab6f0848f10c2..15bf6a4871044 100644 --- a/src/engraving/dom/note.cpp +++ b/src/engraving/dom/note.cpp @@ -1542,6 +1542,17 @@ bool Note::acceptDrop(EditData& data) const const Staff* st = staff(); bool isTablature = st->isTabStaff(tick()); bool tabFingering = st->staffTypeForElement(this)->showTabFingering(); + + if (type == ElementType::STRING_TUNINGS) { + staff_idx_t staffIdx; + Segment* seg; + if (!score()->pos2measure(data.pos, &staffIdx, 0, &seg, 0)) { + return false; + } + + return chord()->measure()->canAddStringTunings(staffIdx); + } + return type == ElementType::ARTICULATION || type == ElementType::ORNAMENT || type == ElementType::FERMATA @@ -1823,6 +1834,9 @@ EngravingItem* Note::drop(EditData& data) toChordLine(e)->setNote(this); return ch->drop(data); + case ElementType::STRING_TUNINGS: + return ch->measure()->drop(data); + default: Spanner* spanner; if (e->isSpanner() && (spanner = toSpanner(e))->anchor() == Spanner::Anchor::NOTE) { diff --git a/src/engraving/dom/rest.cpp b/src/engraving/dom/rest.cpp index 0eac834bddf3e..78795cb014805 100644 --- a/src/engraving/dom/rest.cpp +++ b/src/engraving/dom/rest.cpp @@ -206,6 +206,16 @@ bool Rest::acceptDrop(EditData& data) const return true; } + if (type == ElementType::STRING_TUNINGS) { + staff_idx_t staffIdx; + Segment* seg; + if (!score()->pos2measure(data.pos, &staffIdx, 0, &seg, 0)) { + return false; + } + + return measure()->canAddStringTunings(staffIdx); + } + // prevent 'hanging' slurs, avoid crash on tie static const std::set ignoredTypes { ElementType::SLUR, @@ -268,6 +278,9 @@ EngravingItem* Rest::drop(EditData& data) score()->undoAddElement(e); return e; + case ElementType::STRING_TUNINGS: + return measure()->drop(data); + default: return ChordRest::drop(data); } diff --git a/src/engraving/dom/stringtunings.cpp b/src/engraving/dom/stringtunings.cpp index 53d70ea31896d..907a7b1548a38 100644 --- a/src/engraving/dom/stringtunings.cpp +++ b/src/engraving/dom/stringtunings.cpp @@ -240,7 +240,7 @@ bool StringTunings::noStringVisible() const String StringTunings::generateText() const { const StringData* stringData = this->stringData(); - if (stringData->isNull()) { + if (!stringData || stringData->isNull()) { return u""; } diff --git a/src/engraving/rendering/dev/tlayout.cpp b/src/engraving/rendering/dev/tlayout.cpp index 0ed51af6100de..c6cfbd984b28f 100644 --- a/src/engraving/rendering/dev/tlayout.cpp +++ b/src/engraving/rendering/dev/tlayout.cpp @@ -369,7 +369,7 @@ void TLayout::layoutItem(EngravingItem* item, LayoutContext& ctx) layoutSticking(item_cast(item), static_cast(ldata)); break; case ElementType::STRING_TUNINGS: - layout(item_cast(item), ctx); + layoutStringTunings(item_cast(item), ctx); break; case ElementType::SYMBOL: layoutSymbol(item_cast(item), static_cast(ldata), ctx); @@ -4973,10 +4973,11 @@ void TLayout::layoutStretched(StretchedBend* item, LayoutContext& ctx) item->setPos(0.0, 0.0); } -void TLayout::layout(StringTunings* item, LayoutContext& ctx) +void TLayout::layoutStringTunings(StringTunings* item, LayoutContext& ctx) { item->updateText(); - layoutTextBase(item, ctx); + + TLayout::layoutBaseTextBase(item, ctx); if (item->noStringVisible()) { double spatium = item->spatium(); @@ -5010,8 +5011,11 @@ void TLayout::layout(StringTunings* item, LayoutContext& ctx) for (TextBlock& block : item->mutldata()->blocks) { double xMove = 0.0; - for (auto& it = ++(block.fragments().begin()); it != block.fragments().end(); ++it) { - TextFragment& fragment = *it; + for (TextFragment& fragment : block.fragments()) { + if (block.fragments().front() == fragment) { // skip first + continue; + } + if (fragment.font(item).type() == mu::draw::Font::Type::MusicSymbol) { xMove = secondStringXAlign - fragment.pos.x(); } diff --git a/src/engraving/rendering/dev/tlayout.h b/src/engraving/rendering/dev/tlayout.h index d8d408d952a27..506782b6d26a4 100644 --- a/src/engraving/rendering/dev/tlayout.h +++ b/src/engraving/rendering/dev/tlayout.h @@ -295,7 +295,7 @@ class TLayout static void layoutSticking(const Sticking* item, Sticking::LayoutData* ldata); static void layoutStretchedBend(StretchedBend* item, LayoutContext& ctx); static void layoutStretched(StretchedBend* item, LayoutContext& ctx); - static void layout(StringTunings* item, LayoutContext& ctx); + static void layoutStringTunings(StringTunings* item, LayoutContext& ctx); static void layoutSymbol(const Symbol* item, Symbol::LayoutData* ldata, const LayoutContext& ctx); static void layoutFSymbol(const FSymbol* item, FSymbol::LayoutData* ldata); diff --git a/src/engraving/rendering/stable/tlayout.cpp b/src/engraving/rendering/stable/tlayout.cpp index 838e8bcac84d8..79ffbdf54b5f6 100644 --- a/src/engraving/rendering/stable/tlayout.cpp +++ b/src/engraving/rendering/stable/tlayout.cpp @@ -317,7 +317,7 @@ void TLayout::layoutItem(EngravingItem* item, LayoutContext& ctx) break; case ElementType::STICKING: layout(item_cast(item), ctx); break; - case ElementType::STRING_TUNINGS: layout(item_cast(item), ctx); + case ElementType::STRING_TUNINGS: layoutStringTunings(item_cast(item), ctx); break; case ElementType::SYMBOL: layout(item_cast(item), ctx); break; @@ -4506,10 +4506,59 @@ void TLayout::layoutStretched(StretchedBend* item, LayoutContext& ctx) item->setPos(0.0, 0.0); } -void TLayout::layout(StringTunings* item, LayoutContext& ctx) +void TLayout::layoutStringTunings(StringTunings* item, LayoutContext& ctx) { item->updateText(); - layoutTextBase(item, ctx); + + TLayout::layoutTextBase(item, ctx); + + if (item->noStringVisible()) { + double spatium = item->spatium(); + mu::draw::Font font(item->font()); + + RectF rect; + rect.setTopLeft({ 0, item->ldata()->bbox().y() - font.weight() - spatium * .15 }); + rect.setSize({ font.weight() - spatium, (font.weight() - spatium * .35) * 1.5 }); + + item->setbbox(rect); + } + + for (TextBlock& block : item->mutldata()->blocks) { + for (TextFragment& fragment : block.fragments()) { + mu::draw::Font font = fragment.font(item); + if (font.type() == mu::draw::Font::Type::MusicSymbol) { + // HACK: the music symbol doesn't have a good baseline + // to go with text so we correct it here + const double baselineAdjustment = 0.35 * font.pointSizeF(); + fragment.pos.setY(fragment.pos.y() + baselineAdjustment); + } + } + } + + double secondStringXAlign = 0.0; + for (const TextFragment& fragment : item->fragmentList()) { + if (fragment.font(item).type() == mu::draw::Font::Type::MusicSymbol) { + secondStringXAlign = std::max(secondStringXAlign, fragment.pos.x()); + } + } + + for (TextBlock& block : item->mutldata()->blocks) { + double xMove = 0.0; + for (TextFragment& fragment : block.fragments()) { + if (block.fragments().front() == fragment) { // skip first + continue; + } + + if (fragment.font(item).type() == mu::draw::Font::Type::MusicSymbol) { + xMove = secondStringXAlign - fragment.pos.x(); + } + fragment.pos.setX(fragment.pos.x() + xMove); + } + } + + Segment* parentSegment = item->segment(); + item->move(PointF(-parentSegment->x(), 0.0)); + Autoplace::autoplaceSegmentElement(item, item->mutldata()); } diff --git a/src/engraving/rendering/stable/tlayout.h b/src/engraving/rendering/stable/tlayout.h index eaa2c2b91f045..24e68c047ccc6 100644 --- a/src/engraving/rendering/stable/tlayout.h +++ b/src/engraving/rendering/stable/tlayout.h @@ -290,7 +290,7 @@ class TLayout static void layout(Sticking* item, LayoutContext& ctx); static void layout(StretchedBend* item, LayoutContext& ctx); static void layoutStretched(StretchedBend* item, LayoutContext& ctx); - static void layout(StringTunings* item, LayoutContext& ctx); + static void layoutStringTunings(StringTunings* item, LayoutContext& ctx); static void layout(Symbol* item, LayoutContext& ctx); static void layout(FSymbol* item, LayoutContext& ctx); diff --git a/src/notation/view/internal/stringtuningssettingsmodel.cpp b/src/notation/view/internal/stringtuningssettingsmodel.cpp index 699918f454c43..c8d469604f4e9 100644 --- a/src/notation/view/internal/stringtuningssettingsmodel.cpp +++ b/src/notation/view/internal/stringtuningssettingsmodel.cpp @@ -124,7 +124,8 @@ bool StringTuningsSettingsModel::setStringValue(int stringIndex, const QString& StringTuningsItem* item = m_strings.at(stringIndex); - int value = engraving::string2pitch(stringValue); + QString _stringValue = fixStringValue(stringValue); + int value = engraving::string2pitch(_stringValue); if (value == -1) { item->valueChanged(); return false; @@ -144,22 +145,26 @@ bool StringTuningsSettingsModel::setStringValue(int stringIndex, const QString& bool StringTuningsSettingsModel::canIncreaseStringValue(const QString& stringValue) const { - return engraving::string2pitch(stringValue) != -1; + QString value = fixStringValue(stringValue); + return engraving::string2pitch(value) != -1; } QString StringTuningsSettingsModel::increaseStringValue(const QString& stringValue) { - return engraving::pitch2string(engraving::string2pitch(stringValue) + 1); + QString value = fixStringValue(stringValue); + return engraving::pitch2string(engraving::string2pitch(value) + 1); } bool StringTuningsSettingsModel::canDecreaseStringValue(const QString& stringValue) const { - return engraving::string2pitch(stringValue) != -1; + QString value = fixStringValue(stringValue); + return engraving::string2pitch(value) != -1; } QString StringTuningsSettingsModel::decreaseStringValue(const QString& stringValue) { - return engraving::pitch2string(engraving::string2pitch(stringValue) - 1); + QString value = fixStringValue(stringValue); + return engraving::pitch2string(engraving::string2pitch(value) - 1); } QVariantList StringTuningsSettingsModel::presets(bool withCustom) const @@ -304,21 +309,23 @@ void StringTuningsSettingsModel::updateStrings() m_strings.clear(); for (const QVariant& _preset : presets) { - if (_preset.toMap()["text"].toString() != currentPreset) { - continue; - } - - QVariantList valueList = _preset.toMap()["value"].toList(); - for (int i = 0; i < valueList.size(); ++i) { - StringTuningsItem* item = new StringTuningsItem(this); - - item->blockSignals(true); - item->setShow(true); - item->setNumber(QString::number(i + 1)); - item->setValue(valueList[i].toInt()); - item->blockSignals(false); + if (_preset.toMap()["text"].toString() == currentPreset) { + QVariantList valueList = _preset.toMap()["value"].toList(); + int numOfStrings = valueList.size(); + for (int i = 0; i < numOfStrings; ++i) { + int valueIndex = numOfStrings - i - 1; + StringTuningsItem* item = new StringTuningsItem(this); + + item->blockSignals(true); + item->setShow(true); + item->setNumber(QString::number(i + 1)); + item->setValue(valueList[valueIndex].toInt()); + item->blockSignals(false); + + m_strings.push_back(item); + } - m_strings.push_back(item); + break; } } @@ -399,6 +406,27 @@ void StringTuningsSettingsModel::doSetCurrentPreset(const QString& preset) emit currentPresetChanged(); } +QString StringTuningsSettingsModel::fixStringValue(const QString& stringValue) const +{ + if (stringValue.isEmpty()) { + return QString(); + } + + QString value = stringValue[0]; + for (int i = 1; i < stringValue.size(); ++i) { + QChar symbol = stringValue[i].toLower(); + if (symbol == "b") { + value.append("♭"); + } else if (symbol == "#") { + value.append("♯"); + } else { + value.append(symbol); + } + } + + return value; +} + StringTuningsItem::StringTuningsItem(QObject* parent) : QObject(parent) { diff --git a/src/notation/view/internal/stringtuningssettingsmodel.h b/src/notation/view/internal/stringtuningssettingsmodel.h index 4be856c133d96..08620af084d94 100644 --- a/src/notation/view/internal/stringtuningssettingsmodel.h +++ b/src/notation/view/internal/stringtuningssettingsmodel.h @@ -92,6 +92,8 @@ class StringTuningsSettingsModel : public AbstractElementPopupModel void doSetCurrentPreset(const QString& preset); + QString fixStringValue(const QString& stringValue) const; + QList m_strings; std::string m_itemId;