Skip to content
166 changes: 76 additions & 90 deletions src/appshell/qml/MuseScore/AppShell/AboutDialog.qml
Original file line number Diff line number Diff line change
Expand Up @@ -33,134 +33,120 @@ StyledDialogView {

title: qsTrc("appshell/about", "About MuseScore Studio")

contentHeight: 424
contentWidth: 480
contentHeight: content.implicitHeight
contentWidth: 400

margins: 40

AboutModel {
id: aboutModel
}

ColumnLayout {
anchors.fill: parent
spacing: 30

ColumnLayout {
id: content

Layout.fillWidth: true
Layout.fillHeight: true
Layout.topMargin: 36
Layout.leftMargin: 40
Layout.rightMargin: 40
id: content

spacing: 32
anchors.fill: parent
spacing: 32

Image {
id: logo
Layout.alignment: Qt.AlignHCenter
Image {
id: logo
Layout.alignment: Qt.AlignHCenter

source: "resources/mu_logo.svg"
sourceSize: Qt.size(100, 100)
source: "resources/mu_logo.svg"
sourceSize: Qt.size(100, 100)

MouseArea {
anchors.fill: parent
MouseArea {
anchors.fill: parent

property int clickCount: 0
property int clickCount: 0

onClicked: {
clickCount++
onClicked: {
clickCount++

if (clickCount % 3 == 0) {
aboutModel.toggleDevMode()
}
if (clickCount % 3 == 0) {
aboutModel.toggleDevMode()
}
}
}
}

Column {
spacing: 8
Layout.fillWidth: true
Column {
spacing: 8
Layout.fillWidth: true

StyledTextLabel {
anchors.horizontalCenter: parent.horizontalCenter
text: qsTrc("appshell/about", "Version:") + " " + aboutModel.museScoreVersion()
font: ui.theme.bodyBoldFont
}
StyledTextLabel {
anchors.horizontalCenter: parent.horizontalCenter
text: qsTrc("appshell/about", "Version: %1").arg(aboutModel.museScoreVersion())
font: ui.theme.bodyBoldFont
}

Row {
spacing: 4
anchors.horizontalCenter: parent.horizontalCenter
Row {
spacing: 4
anchors.horizontalCenter: parent.horizontalCenter

StyledTextLabel {
anchors.verticalCenter: parent.verticalCenter
text: qsTrc("appshell/about", "Revision:") + " " + aboutModel.museScoreRevision()
}
StyledTextLabel {
anchors.verticalCenter: parent.verticalCenter
text: qsTrc("appshell/about", "Revision: %1").arg(aboutModel.museScoreRevisionLink())
}

FlatButton {
anchors.verticalCenter: parent.verticalCenter
icon: IconCode.COPY
FlatButton {
anchors.verticalCenter: parent.verticalCenter
icon: IconCode.COPY

onClicked: {
aboutModel.copyRevisionToClipboard()
}
onClicked: {
aboutModel.copyRevisionToClipboard()
}
}
}
}

StyledTextLabel {
Layout.fillWidth: true
text: {
let museScoreUrl = aboutModel.museScoreUrl()

//: %1 will be a link to the MuseScore website
let line1 = qsTrc("appshell/about", "Visit %1 for new versions and more information.")
.arg(`<a href="${museScoreUrl.url}">${museScoreUrl.displayName}</a>`)
StyledTextLabel {
Layout.fillWidth: true

let line2 = qsTrc("appshell/about", "Get <a href=\"%1\">help</a> with the program or <a href=\"%2\">contribute</a> to its development.")
.arg(aboutModel.museScoreForumUrl().url)
.arg(aboutModel.museScoreContributionUrl().url)
text: qsTrc("appshell/about", "Build date: %1")
.arg(aboutModel.museScoreBuildDateTime())

return line1 + "<br>" + line2
}
wrapMode: Text.WordWrap
maximumLineCount: 3
}

StyledTextLabel {
Layout.fillWidth: true
wrapMode: Text.WordWrap
maximumLineCount: 2
}

text: qsTrc("appshell/about", "For privacy information, see our <a href=\"%1\">privacy policy</a>.")
.arg(aboutModel.museScorePrivacyPolicyUrl().url)
StyledTextLabel {
Layout.fillWidth: true
text: {
let museScoreUrl = aboutModel.museScoreUrl()

wrapMode: Text.WordWrap
maximumLineCount: 3
}
//: %1 will be a link to the MuseScore website
let line1 = qsTrc("appshell/about", "Visit %1 for new versions and more information.")
.arg(`<a href="${museScoreUrl.url}">${museScoreUrl.displayName}</a>`)

StyledTextLabel {
Layout.fillWidth: true
text: qsTrc("appshell/about", "Copyright © 1999-2026 MuseScore Limited and others.\nPublished under the <a href=\"%1\">GNU General Public License version 3</a>.")
.arg("https://www.gnu.org/licenses/gpl-3.0.html")
.replace("\n", "<br>")
let line2 = qsTrc("appshell/about", "Get <a href=\"%1\">help</a> with the program or <a href=\"%2\">contribute</a> to its development.")
.arg(aboutModel.museScoreForumUrl().url)
.arg(aboutModel.museScoreContributionUrl().url)

wrapMode: Text.WordWrap
maximumLineCount: 3
return line1 + "<br>" + line2
}
wrapMode: Text.WordWrap
maximumLineCount: 3
}

RowLayout {
Layout.alignment: Qt.AlignRight
Layout.rightMargin: 16
Layout.bottomMargin: 16
StyledTextLabel {
Layout.fillWidth: true

spacing: 12
text: qsTrc("appshell/about", "For privacy information, see our <a href=\"%1\">privacy policy</a>.")
.arg(aboutModel.museScorePrivacyPolicyUrl().url)

FlatButton {
text: qsTrc("global", "OK")
wrapMode: Text.WordWrap
maximumLineCount: 3
}

onClicked: {
root.hide()
}
}
StyledTextLabel {
Layout.fillWidth: true
text: qsTrc("appshell/about", "Copyright © 1999-2026 MuseScore Limited and others.\nPublished under the <a href=\"%1\">GNU General Public License version 3</a>.")
.arg("https://www.gnu.org/licenses/gpl-3.0.html")
.replace("\n", "<br>")

wrapMode: Text.WordWrap
maximumLineCount: 3
}
}
}
58 changes: 46 additions & 12 deletions src/appshell/qml/MuseScore/AppShell/aboutmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@

using namespace mu::appshell;

//! NOTE: Should match value in projectpropertiesmodel.cpp
static const QString GITHUB_REVISION_URL("https://github.com/musescore/MuseScore/commit/");

AboutModel::AboutModel(QObject* parent)
: QObject(parent), muse::Contextable(muse::iocCtxForQmlObject(this))
{
Expand All @@ -43,9 +46,33 @@ QString AboutModel::museScoreVersion() const
: version;
}

QString AboutModel::museScoreRevision() const
QString AboutModel::museScoreRevisionLink() const
{
const QString rev = QString::fromStdString(configuration()->museScoreRevision());
if (rev.isEmpty()) {
return muse::qtrc("global", "Unknown");
}
const QString revUrl = GITHUB_REVISION_URL + rev;
return QString("<a href=\"%1\">%2</a>").arg(revUrl, rev);
}

QString AboutModel::museScoreBuildDateTime() const
{
return QString::fromStdString(configuration()->museScoreRevision());
auto buildDateISO = []() -> QString {
// Attempt to convert __DATE__ into ISO 8601 format (YYYY-MM-DD)
QDate date = QDate::fromString(__DATE__, "MMM dd yyyy");
if (!date.isValid()) {
date = QDate::fromString(__DATE__, "MMM d yyyy");
}
return date.isValid() ? date.toString(Qt::ISODate) : __DATE__;
};

QString dateTime;
dateTime += buildDateISO();
dateTime += " ";
dateTime += __TIME__;

return dateTime;
}

QVariantMap AboutModel::museScoreUrl() const
Expand Down Expand Up @@ -86,16 +113,23 @@ QVariantMap AboutModel::musicXMLLicenseDeedUrl() const

void AboutModel::copyRevisionToClipboard() const
{
QGuiApplication::clipboard()->setText(
QString("OS: %1, Arch.: %2, MuseScore Studio version (%3-bit): %4-%5, revision: github-musescore-musescore-%6")
.arg(QSysInfo::prettyProductName()
+ ((QSysInfo::productType() == "windows" && (QSysInfo::productVersion() == "10" || QSysInfo::productVersion() == "11"))
? " or later" : ""))
.arg(QSysInfo::currentCpuArchitecture())
.arg(QSysInfo::WordSize)
.arg(application()->version().toString())
.arg(application()->build())
.arg(application()->revision()));
QString osName = QSysInfo::prettyProductName();
if (QSysInfo::productType() == "windows" && (QSysInfo::productVersion() == "10" || QSysInfo::productVersion() == "11")) {
osName += " or later";
}

QString forClipboard = QString("OS: %1, Arch.: %2, MuseScore Studio version (%3-bit): %4-%5")
.arg(osName, QSysInfo::currentCpuArchitecture())
.arg(QSysInfo::WordSize)
.arg(application()->version().toString(), application()->build());

const QString rev = QString::fromStdString(configuration()->museScoreRevision());
if (!rev.isEmpty()) {
const QString revUrl = GITHUB_REVISION_URL + rev;
forClipboard += QString(", revision: [%1](%2)").arg(rev, revUrl);
}

QGuiApplication::clipboard()->setText(forClipboard);
}

void AboutModel::toggleDevMode()
Expand Down
3 changes: 2 additions & 1 deletion src/appshell/qml/MuseScore/AppShell/aboutmodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ class AboutModel : public QObject, public muse::Contextable
explicit AboutModel(QObject* parent = nullptr);

Q_INVOKABLE QString museScoreVersion() const;
Q_INVOKABLE QString museScoreRevision() const;
Q_INVOKABLE QString museScoreRevisionLink() const;
Q_INVOKABLE QString museScoreBuildDateTime() const;
Q_INVOKABLE QVariantMap museScoreUrl() const;
Q_INVOKABLE QVariantMap museScoreForumUrl() const;
Q_INVOKABLE QVariantMap museScoreContributionUrl() const;
Expand Down
11 changes: 6 additions & 5 deletions src/engraving/rendering/score/headerfooterlayout.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -357,14 +357,15 @@ TextBlock HeaderFooterLayout::replaceTextMacros(LayoutContext& ctx, const Page*
break;
case 'r':
if (page->score()->dirty()) {
static String revision; // FIXME
newFragments.back().text += revision;
newFragments.back().text += application()->revision();
} else {
int rev = page->score()->mscoreRevision();
if (rev > 99999) { // MuseScore 1.3 is decimal 5702, 2.0 and later uses a 7-digit hex SHA
newFragments.back().text += String::number(rev, 16);
} else {
if (rev > 0 && rev <= 5709) { // MuseScore 1.x and earlier used decimal numbers, referring to a commit on SourceForge.net, the largest being 5709
newFragments.back().text += String::number(rev, 10);
} else if (rev > 0xffffff) { // MuseScore 2.0 and later use a >= 7-digit hex SHA, referring to a commit on GitHub.com
newFragments.back().text += String::number(rev, 16);
} else { // unknown, like in a self-built development version, or in a very old version of MuseScore before the revision number was tracked
newFragments.back().text += String::number(rev, 10); // or "" or "Unknown"?
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
}
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ using namespace muse::modularity;
using namespace mu::notation;
using namespace mu::project;

//! NOTE: Should match value in aboutmodel.cpp
static const QString GITHUB_REVISION_URL("https://github.com/musescore/MuseScore/commit/");
static const QString SOURCEFORGE_REVISION_URL("https://sourceforge.net/p/mscore/code/");

ProjectPropertiesModel::ProjectPropertiesModel(QObject* parent)
: QAbstractListModel(parent), muse::Contextable(muse::iocCtxForQmlObject(this))
{
Expand Down Expand Up @@ -159,15 +163,23 @@ QString ProjectPropertiesModel::version() const

QString ProjectPropertiesModel::revision() const
{
int rev = m_projectMetaInfo.musescoreRevision;
QString revision;
if (rev > 99999) { // MuseScore 1.3 is decimal 5702, 2.0 and later uses a 7-digit hex SHA
revision = QString::number(rev, 16);
} else {
revision = QString::number(rev, 10);
const int rev = m_projectMetaInfo.musescoreRevision;
if (rev > 0 && rev <= 5709) {
// MuseScore 1.x and earlier used decimal numbers, referring to a commit on SourceForge.net, the largest being 5709
const QString revText = QString::number(rev, 10);
const QString revUrl = SOURCEFORGE_REVISION_URL + revText;
return QString("<a href=\"%1\">%2</a>").arg(revUrl, revText);
}

if (rev > 0xffffff) {
// MuseScore 2.0 and later use a >= 7-digit hex SHA, referring to a commit on GitHub.com
const QString revText = QString::number(rev, 16);
const QString revUrl = GITHUB_REVISION_URL + revText;
return QString("<a href=\"%1\">%2</a>").arg(revUrl, revText);
}

return revision;
// unknown, like in a self-built development version, or in a very old version of MuseScore before the revision number was tracked
return muse::qtrc("global", "Unknown");

@Jojo-Schmitz Jojo-Schmitz Aug 10, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure we'd want a translated text getting placed in the score's metadata?

And is inconsistent vs. how it is dealt with for header/footer

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm - if I understand you correctly this doesn't actually modify the score's metadata though? It's purely used for UI purposes in the project properties dialog when we encounter a revision we don't recognise.

@Jojo-Schmitz Jojo-Schmitz Aug 10, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't the revision also written into the score?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is, but not using this method (it uses application()->revision() see here).

@Jojo-Schmitz Jojo-Schmitz Aug 11, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see, you're right.

Remaining question is whether to convert an unknown revision number for header/footer, it might be better to not even try (and print "0" in almost all cases, or some arbitrary and meaningless number between 5710 and 0xfffffe) and just print nothing instead?

}

QString ProjectPropertiesModel::apiLevel() const
Expand Down
Loading