Add other columns in scores list view
This commit is contained in:
parent
8c2ef3ef65
commit
253c197fa9
13 changed files with 179 additions and 18 deletions
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue