diff --git a/pyproject.toml b/pyproject.toml index 8900464..7a8f881 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -75,6 +75,8 @@ files = [ "ui/qmls/App.qml", "ui/qmls/AppMain.qml", + "ui/qmls/Components/PlayResultDelegate.qml", + "ui/viewmodels/overview.py", "ui/qmls/Overview.qml", diff --git a/ui/qmls/Components/PlayResultDelegate.qml b/ui/qmls/Components/PlayResultDelegate.qml new file mode 100644 index 0000000..509dbb1 --- /dev/null +++ b/ui/qmls/Components/PlayResultDelegate.qml @@ -0,0 +1,124 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuick.VectorImage + +import "../libs/formatters.mjs" as Formatters + +RowLayout { + id: root + + required property var playResult + property alias pr: root.playResult + + spacing: 8 + + SystemPalette { + id: systemPalette + } + + component PFLLabel: RowLayout { + required property string label + required property var value + property color color: systemPalette.text + + spacing: 0.5 + + Label { + Layout.alignment: Qt.AlignBaseline + + text: parent.label + font.pointSize: 8 + font.bold: true + color: parent.color + } + + Label { + Layout.alignment: Qt.AlignBaseline + text: parent.value ?? '-' + color: parent.color + } + } + + function getGradeIcon(gradeLabel: string): string { + const scheme = Application.styleHints.colorScheme == Qt.ColorScheme.Dark ? 'dark' : 'light'; + const filenameMap = { + 'EX+': 'ex-plus', + 'EX': 'ex', + 'AA': 'aa', + 'A': 'a', + 'B': 'b', + 'C': 'c', + 'D': 'd' + }; + + let filenameBase = filenameMap[gradeLabel]; + if (scheme === 'dark') { + filenameBase += '-dark'; + } + + return `qrc:/images/grades/${filenameBase}.svg`; + } + + TextMetrics { + id: gradeTextMetrics + text: 'EX+' + + font.pointSize: 18 + font.bold: true + } + + VectorImage { + id: gradeIcon + Layout.preferredWidth: gradeTextMetrics.width + Layout.preferredHeight: gradeTextMetrics.width + + fillMode: VectorImage.PreserveAspectFit + preferredRendererType: VectorImage.CurveRenderer + source: root.getGradeIcon(Formatters.scoreToGrade(root.pr.score)) + } + + ColumnLayout { + Layout.fillWidth: true + spacing: 1 + + Label { + Layout.fillWidth: true + text: root.pr.score + font.pointSize: 16 + } + + RowLayout { + Layout.fillWidth: true + Layout.leftMargin: 2 + spacing: 5 + + PFLLabel { + label: 'P' + value: root.pr.pure + color: appTheme.pure + } + + PFLLabel { + label: 'F' + value: root.pr.far + color: appTheme.far + } + + PFLLabel { + label: 'L' + value: root.pr.lost + color: appTheme.lost + } + } + + PFLLabel { + Layout.fillWidth: true + Layout.leftMargin: 2 + + label: 'MR' + value: root.pr.maxRecall + } + } +} diff --git a/ui/qmls/Components/qmldir b/ui/qmls/Components/qmldir index 3a76167..47f6c3e 100644 --- a/ui/qmls/Components/qmldir +++ b/ui/qmls/Components/qmldir @@ -1,6 +1,9 @@ module Components + internal SelectorBase SelectorBase.qml DirectorySelector 1.0 DirectorySelector.qml FileSelector 1.0 FileSelector.qml SectionTitle 1.0 SectionTitle.qml + +PlayResultDelegate 1.0 PlayResultDelegate.qml diff --git a/ui/qmls/libs/formatters.mjs b/ui/qmls/libs/formatters.mjs new file mode 100644 index 0000000..280243c --- /dev/null +++ b/ui/qmls/libs/formatters.mjs @@ -0,0 +1,18 @@ +export function scoreToGrade(score) { + const gradeThresholds = [ + { minimum: 9900000, grade: "EX+" }, + { minimum: 9800000, grade: "EX" }, + { minimum: 9500000, grade: "AA" }, + { minimum: 9200000, grade: "A" }, + { minimum: 8900000, grade: "B" }, + { minimum: 8600000, grade: "C" }, + ]; + + for (const threshold of gradeThresholds) { + if (score >= threshold.minimum) { + return threshold.grade; + } + } + + return "D"; +}