Add other columns in scores list view

This commit is contained in:
Casper Jeukendrup 2023-07-31 03:45:51 +02:00
parent 8c2ef3ef65
commit 253c197fa9
No known key found for this signature in database
GPG key ID: 6C571BEF59E722DD
13 changed files with 179 additions and 18 deletions

View file

@ -123,7 +123,9 @@ struct ScoresList {
int id = 0;
QString title;
QDateTime lastModified;
size_t fileSize = 0;
QString thumbnailUrl;
int viewCount = 0;
};
std::vector<Item> items;

View file

@ -288,7 +288,9 @@ mu::async::Promise<ScoresList> MuseScoreComService::downloadScoresList(int score
item.id = itemObj.value("id").toInt();
item.title = itemObj.value("title").toString();
item.lastModified = QDateTime::fromSecsSinceEpoch(itemObj.value("date_updated").toInt());
item.fileSize = itemObj.value("current_revision").toObject().value("file_size").toInt();
item.thumbnailUrl = itemObj.value("thumbnails").toObject().value("small").toString();
item.viewCount = itemObj.value("view_count").toInt();
result.items.push_back(item);
}

View file

@ -78,3 +78,27 @@ String DataFormatter::formatTimeSince(const Date& date)
return mtrc("global", "%n year(s) ago", nullptr, years);
}
String DataFormatter::formatFileSize(size_t size)
{
if (size >= 1024 * 1024 * 1024) {
double gb = double(size) / (1024 * 1024 * 1024);
//: Abbreviation of "gigabyte", used to indicate file size
return mtrc("global", "%1 GB", "gigabyte").arg(formatReal(gb, 2));
}
if (size >= 1024 * 1024) {
double mb = double(size) / (1024 * 1024);
//: Abbreviation of "megabyte", used to indicate file size
return mtrc("global", "%1 MB", "megabyte").arg(formatReal(mb, 1));
}
if (size >= 1024) {
double kb = double(size) / 1024;
//: Abbreviation of "kilobyte", used to indicate file size
return mtrc("global", "%1 KB", "kilobyte").arg(formatReal(kb, 0));
}
//: Used to indicate file size. Ideally, keep the translation short; feel free to use an abbreviation.
return mtrc("global", "%n bytes", "", size);
}

View file

@ -32,6 +32,7 @@ public:
static double roundDouble(const double& val, const int decimals = 2);
static String formatReal(double val, int prec = 2);
static String formatTimeSince(const Date& date);
static String formatFileSize(size_t size);
};
}

View file

@ -21,6 +21,7 @@
*/
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import MuseScore.Ui 1.0
import MuseScore.UiComponents 1.0
@ -145,6 +146,77 @@ ScoresView {
navigation.name: "OnlineScoresList"
navigation.accessible.name: qsTrc("project", "Online scores list")
columns: [
// TODO: visbility column
ScoresListView.ColumnItem {
//: Stands for "Last time that this score was modified".
//: Used as the header of this column in the scores list.
header: qsTrc("project", "Modified")
width: function (parentWidth) {
let parentWidthExclusingSpacing = parentWidth - (list.columns.length - 1) * list.view.columnSpacing;
return 0.18 * parentWidthExclusingSpacing
}
delegate: StyledTextLabel {
// TODO: accessibility
text: score.timeSinceModified ?? ""
font.capitalization: Font.AllUppercase
horizontalAlignment: Text.AlignLeft
}
},
ScoresListView.ColumnItem {
header: qsTrc("project", "Size", "file size")
width: function (parentWidth) {
let parentWidthExclusingSpacing = parentWidth - (list.columns.length - 1) * list.view.columnSpacing;
return 0.18 * parentWidthExclusingSpacing
}
delegate: StyledTextLabel {
// TODO: accessibility
text: score.fileSize ?? ""
font.capitalization: Font.AllUppercase
horizontalAlignment: Text.AlignLeft
}
},
ScoresListView.ColumnItem {
//: Stands for "The number of times this score was viewed on MuseScore.com".
//: Used as the header of this column in the scores list.
header: qsTrc("project", "Views", "number of views")
width: function (parentWidth) {
let parentWidthExclusingSpacing = parentWidth - (list.columns.length - 1) * list.view.columnSpacing;
return Math.max(0.10 * parentWidthExclusingSpacing, 76)
}
delegate: RowLayout {
visible: !label.isEmpty
spacing: 8
StyledIconLabel {
iconCode: IconCode.EYE_OPEN
}
StyledTextLabel {
id: label
Layout.fillWidth: true
// TODO: accessibility
text: score.cloudViewCount ?? ""
font.capitalization: Font.AllUppercase
horizontalAlignment: Text.AlignLeft
}
}
}
]
view.footer: cloudScoresModel.state === CloudScoresModel.Loading
? busyIndicatorComp : null
@ -153,7 +225,7 @@ ScoresView {
Item {
width: ListView.view ? ListView.view.width : 0
height: view.rowHeight
height: list.view.rowHeight
StyledBusyIndicator {
id: indicator

View file

@ -52,10 +52,50 @@ ScoresView {
id: listComp
ScoresView.List {
id: list
showNewScoreItem: true
navigation.name: "RecentScoresList"
navigation.accessible.name: qsTrc("project", "Recent scores list")
columns: [
ScoresListView.ColumnItem {
//: Stands for "Last time that this score was modified".
//: Used as the header of this column in the scores list.
header: qsTrc("project", "Modified")
width: function (parentWidth) {
let parentWidthExclusingSpacing = parentWidth - (list.columns.length - 1) * list.view.columnSpacing;
return 0.25 * parentWidthExclusingSpacing
}
delegate: StyledTextLabel {
// TODO: accessibility
text: score.timeSinceModified ?? ""
font.capitalization: Font.AllUppercase
horizontalAlignment: Text.AlignLeft
}
},
ScoresListView.ColumnItem {
header: qsTrc("project", "Size", "file size")
width: function (parentWidth) {
let parentWidthExclusingSpacing = parentWidth - (list.columns.length - 1) * list.view.columnSpacing;
return 0.15 * parentWidthExclusingSpacing
}
delegate: StyledTextLabel {
// TODO: accessibility
text: score.fileSize ?? ""
font.capitalization: Font.AllUppercase
horizontalAlignment: Text.AlignLeft
}
}
]
}
}
}

View file

@ -31,7 +31,7 @@ ListItemBlank {
id: root
required property var score
property var columns: columnsRepeater.model
property alias columns: columnsRepeater.model
property alias thumbnailComponent: thumbnailLoader.sourceComponent

View file

@ -31,7 +31,7 @@ Item {
id: root
property AbstractScoresModel model
property var columns: []
property list<ColumnItem> columns
property alias showNewScoreItem: newScoreItem.visible
property string searchText
@ -45,12 +45,14 @@ Item {
signal createNewScoreRequested()
signal openScoreRequested(var scorePath, var displayName)
QtObject {
id: prv
component ColumnItem : QtObject {
property string header
readonly property real itemInset: 12
readonly property real rowHeight: 64
readonly property real columnSpacing: 44
property var width: function (parentWidth) {
return parentWidth / 5
}
property Component delegate
}
SortFilterProxyModel {
@ -86,10 +88,10 @@ Item {
id: newScoreItem
Layout.fillWidth: true
implicitHeight: prv.rowHeight
implicitHeight: view.rowHeight
visible: false
itemInset: prv.itemInset
itemInset: view.itemInset
showBottomBorder: false
navigation.panel: navPanel
@ -123,7 +125,7 @@ Item {
Layout.fillWidth: true
Layout.fillHeight: true
visible: view.count > 0
visible: view.count > 0 || view.header || view.footer
ColumnLayout {
id: listViewColumn
@ -134,10 +136,10 @@ Item {
// Column headers
RowLayout {
Layout.preferredHeight: 44
Layout.leftMargin: prv.itemInset
Layout.rightMargin: prv.itemInset
Layout.leftMargin: view.itemInset
Layout.rightMargin: view.itemInset
spacing: prv.columnSpacing
spacing: view.columnSpacing
StyledTextLabel {
Layout.fillWidth: true
@ -176,7 +178,9 @@ Item {
bottomMargin: bottomGradient.height
readonly property real rowHeight: prv.rowHeight
readonly property real itemInset: 12
readonly property real rowHeight: 64
readonly property real columnSpacing: 44
ScrollBar.vertical: StyledScrollBar {
parent: root
@ -196,9 +200,9 @@ Item {
columns: root.columns
itemInset: prv.itemInset
implicitHeight: prv.rowHeight
columnSpacing: prv.columnSpacing
itemInset: view.itemInset
implicitHeight: view.rowHeight
columnSpacing: view.columnSpacing
navigation.panel: navPanel
navigation.row: index + 1

View file

@ -28,12 +28,15 @@ using namespace mu::project;
const QString AbstractScoresModel::NAME_KEY("name");
const QString AbstractScoresModel::PATH_KEY("path");
const QString AbstractScoresModel::SUFFIX_KEY("suffix");
const QString AbstractScoresModel::FILE_SIZE_KEY("fileSize");
const QString AbstractScoresModel::THUMBNAIL_URL_KEY("thumbnailUrl");
const QString AbstractScoresModel::TIME_SINCE_MODIFIED_KEY("timeSinceModified");
const QString AbstractScoresModel::IS_CREATE_NEW_KEY("isCreateNew");
const QString AbstractScoresModel::IS_NO_RESULTS_FOUND_KEY("isNoResultsFound");
const QString AbstractScoresModel::IS_CLOUD_KEY("isCloud");
const QString AbstractScoresModel::CLOUD_SCORE_ID_KEY("scoreId");
const QString AbstractScoresModel::CLOUD_VISIBILITY_KEY("cloudVisibility");
const QString AbstractScoresModel::CLOUD_VIEW_COUNT_KEY("cloudViewCount");
AbstractScoresModel::AbstractScoresModel(QObject* parent)
: QAbstractListModel(parent)

View file

@ -56,12 +56,15 @@ protected:
static const QString NAME_KEY;
static const QString PATH_KEY;
static const QString SUFFIX_KEY;
static const QString FILE_SIZE_KEY;
static const QString THUMBNAIL_URL_KEY;
static const QString TIME_SINCE_MODIFIED_KEY;
static const QString IS_CREATE_NEW_KEY;
static const QString IS_NO_RESULTS_FOUND_KEY;
static const QString IS_CLOUD_KEY;
static const QString CLOUD_SCORE_ID_KEY;
static const QString CLOUD_VISIBILITY_KEY;
static const QString CLOUD_VIEW_COUNT_KEY;
std::vector<QVariantMap> m_items;
};

View file

@ -126,12 +126,15 @@ void CloudScoresModel::loadItemsIfNecessary()
obj[NAME_KEY] = item.title;
obj[PATH_KEY] = configuration()->cloudProjectPath(item.id).toQString();
obj[SUFFIX_KEY] = "";
obj[FILE_SIZE_KEY] = (item.fileSize > 0) ? DataFormatter::formatFileSize(item.fileSize).toQString() : QString();
obj[IS_CLOUD_KEY] = true;
obj[CLOUD_SCORE_ID_KEY] = item.id;
obj[TIME_SINCE_MODIFIED_KEY] = DataFormatter::formatTimeSince(Date::fromQDate(item.lastModified.date())).toQString();
obj[THUMBNAIL_URL_KEY] = item.thumbnailUrl;
obj[IS_CREATE_NEW_KEY] = false;
obj[IS_NO_RESULTS_FOUND_KEY] = false;
//obj[CLOUD_VISIBILITY_KEY] = item.; TODO
obj[CLOUD_VIEW_COUNT_KEY] = item.viewCount;
m_items.push_back(obj);
}

View file

@ -75,9 +75,14 @@ void RecentScoresModel::updateRecentScores()
std::string suffix = io::suffix(file.path);
bool isSuffixInteresting = suffix != engraving::MSCZ;
RetVal<uint64_t> fileSize = fileSystem()->fileSize(file.path);
QString fileSizeString = (fileSize.ret && fileSize.val > 0) ? DataFormatter::formatFileSize(fileSize.val).toQString() : QString();
obj[NAME_KEY] = file.displayName(isSuffixInteresting);
obj[PATH_KEY] = file.path.toQString();
obj[SUFFIX_KEY] = QString::fromStdString(suffix);
obj[FILE_SIZE_KEY] = fileSizeString;
obj[IS_CLOUD_KEY] = configuration()->isCloudProject(file.path);
obj[CLOUD_SCORE_ID_KEY] = configuration()->cloudScoreIdFromPath(file.path);
obj[TIME_SINCE_MODIFIED_KEY] = DataFormatter::formatTimeSince(io::FileInfo(file.path).lastModified().date()).toQString();

View file

@ -28,6 +28,7 @@
#include "iprojectconfiguration.h"
#include "irecentfilescontroller.h"
#include "io/ifilesystem.h"
namespace mu::project {
class RecentScoresModel : public AbstractScoresModel, public async::Asyncable
@ -36,6 +37,7 @@ class RecentScoresModel : public AbstractScoresModel, public async::Asyncable
INJECT(IProjectConfiguration, configuration)
INJECT(IRecentFilesController, recentFilesController)
INJECT(io::IFileSystem, fileSystem)
public:
RecentScoresModel(QObject* parent = nullptr);