Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/engraving/api/v1/score.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,16 @@ QQmlListProperty<System> Score::systems()
return wrapContainerProperty<System>(this, score()->systems());
}

//---------------------------------------------------------
// Score::spanners
//---------------------------------------------------------

QQmlListProperty<EngravingItem> Score::spanners()
{
m_cachedSpannerList = score()->spannerList();
return wrapContainerProperty<EngravingItem>(this, m_cachedSpannerList);
}

//---------------------------------------------------------
// Score::startCmd
//---------------------------------------------------------
Expand Down
7 changes: 7 additions & 0 deletions src/engraving/api/v1/score.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,9 @@ class Score : public apiv1::ScoreElement, public muse::Injectable
/// List of systems in this score.
/// \since MuseScore 4.6
Q_PROPERTY(QQmlListProperty<apiv1::System> systems READ systems)
/// List of spanners (hairpins, slurs, etc.) in this score.
/// \since MuseScore 4.7
Q_PROPERTY(QQmlListProperty<apiv1::EngravingItem> spanners READ spanners)

muse::Inject<mu::context::IGlobalContext> context = { this };

Expand Down Expand Up @@ -345,12 +348,16 @@ class Score : public apiv1::ScoreElement, public muse::Injectable
QQmlListProperty<apiv1::Staff> staves();
QQmlListProperty<apiv1::Page> pages();
QQmlListProperty<apiv1::System> systems();
QQmlListProperty<apiv1::EngravingItem> spanners();

static const mu::engraving::InstrumentTemplate* instrTemplateFromName(const QString& name); // used by PluginAPI::newScore()
/// \endcond

private:
mu::notation::INotationPtr notation() const;
mu::notation::INotationUndoStackPtr undoStack() const;

// Cache for spanner list to provide stable reference for QML property
mutable std::vector<mu::engraving::Spanner*> m_cachedSpannerList;
};
}
14 changes: 14 additions & 0 deletions src/engraving/dom/score.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4496,6 +4496,20 @@ void Score::addSpanner(Spanner* s, bool computeStartEnd)
}
}

//---------------------------------------------------------
// spannerList
//---------------------------------------------------------

std::vector<Spanner*> Score::spannerList() const
{
std::vector<Spanner*> result;
const std::multimap<int, Spanner*>& spannerMap = m_spanner.map();
for (auto it = spannerMap.begin(); it != spannerMap.end(); ++it) {
result.push_back(it->second);
}
return result;
}

//---------------------------------------------------------
// removeSpanner
//---------------------------------------------------------
Expand Down
1 change: 1 addition & 0 deletions src/engraving/dom/score.h
Original file line number Diff line number Diff line change
Expand Up @@ -964,6 +964,7 @@ class Score : public EngravingObject, public muse::Injectable
const std::multimap<int, Spanner*>& spanner() const { return m_spanner.map(); }
SpannerMap& spannerMap() { return m_spanner; }
const SpannerMap& spannerMap() const { return m_spanner; }
std::vector<Spanner*> spannerList() const; // Return all spanners as a vector for Plugin API
bool isSpannerStartEnd(const Fraction& tick, track_idx_t track) const;
void removeSpanner(Spanner*);
void addSpanner(Spanner*, bool computeStartEnd = true);
Expand Down
55 changes: 55 additions & 0 deletions src/engraving/tests/spanners_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@
#include "engraving/dom/system.h"
#include "engraving/editing/editexcerpt.h"

#include "engraving/api/v1/score.h"
#include "engraving/api/v1/elements.h"

#include "utils/scorerw.h"
#include "utils/scorecomp.h"

Expand Down Expand Up @@ -593,3 +596,55 @@ TEST_F(Engraving_SpannersTests, spanners16)
EXPECT_TRUE(ScoreComp::saveCompareScore(score, u"smallstaff01.mscx", SPANNERS_DATA_DIR + u"smallstaff01-ref.mscx"));
delete score;
}

//---------------------------------------------------------
/// spanners17
/// Test Plugin API score.spanners property
/// Verify that score.spanners exposes all spanners in the score
//---------------------------------------------------------

TEST_F(Engraving_SpannersTests, spanners17_pluginAPI_scoreSpanners)
{
// Load a score file
MasterScore* score = ScoreRW::readScore(SPANNERS_DATA_DIR + u"glissando01.mscx");
EXPECT_TRUE(score);

// Create Plugin API wrapper for the score
apiv1::Score apiScore(score);

// Get spanners using Plugin API
QQmlListProperty<apiv1::EngravingItem> scoreSpanners = apiScore.spanners();

// Basic sanity checks: the property should return a valid list
EXPECT_NE(scoreSpanners.count, nullptr);
EXPECT_NE(scoreSpanners.at, nullptr);

int spannerCount = scoreSpanners.count(&scoreSpanners);
EXPECT_GE(spannerCount, 0) << "Count should be non-negative";

// Get spanners directly from the score for comparison
auto domSpanners = score->spannerList();

// The Plugin API should expose the same number of spanners
EXPECT_EQ(spannerCount, (int)domSpanners.size())
<< "Plugin API should expose all spanners from the score";

// Verify each spanner can be accessed and has valid properties
for (int i = 0; i < spannerCount; i++) {
auto* item = scoreSpanners.at(&scoreSpanners, i);
apiv1::EngravingItem* apiItem = qobject_cast<apiv1::EngravingItem*>(item);
EXPECT_TRUE(apiItem != nullptr) << "Spanner " << i << " should be a valid EngravingItem";

if (apiItem && apiItem->element()) {
// Verify it's actually a spanner
mu::engraving::EngravingItem* elem = apiItem->element();
EXPECT_TRUE(elem->isSpanner()) << "Item " << i << " should be a Spanner";

// Verify we can access the track property (spanners have tracks)
track_idx_t track = elem->track();
EXPECT_GE(track, 0) << "Spanner " << i << " should have a valid track";
}
}

delete score;
}
Loading