feat: checkpoint QML view migration

This commit is contained in:
dela
2026-07-15 11:57:34 +08:00
parent 3002adb7bf
commit b117bbf99e
133 changed files with 23964 additions and 17074 deletions
+297
View File
@@ -0,0 +1,297 @@
import QtQuick
import QtQuick.Controls.Basic
import QtQuick.Layouts
import qEmby
Item {
id: page
Rectangle {
anchors.fill: parent
color: Theme.bgBase
}
ColumnLayout {
anchors.fill: parent
anchors.leftMargin: Theme.spacingLg
anchors.rightMargin: Theme.spacingLg
anchors.topMargin: Theme.spacingLg
anchors.bottomMargin: Theme.spacingXl
spacing: Theme.spacingLg
RowLayout {
Layout.fillWidth: true
spacing: Theme.spacingMd
Text {
text: qsTr("Logs")
color: Theme.textPrimary
font.pixelSize: Theme.fontTitle
font.weight: Font.ExtraBold
}
Item {
Layout.fillWidth: true
}
Text {
text: logsPage.statsText
color: Theme.textMuted
font.pixelSize: Theme.fontBody
}
ThemedButton {
text: logsPage.refreshing ? qsTr("Refreshing…") : qsTr("Refresh")
enabled: !logsPage.refreshing
Accessible.name: qsTr("Refresh log files")
onClicked: logsPage.refresh()
}
}
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: Math.min(190, Math.max(118, page.height * 0.25))
radius: Theme.radiusLg
color: Theme.alpha(Theme.bgSurface, Theme.dark ? 0.64 : 0.94)
border.width: 1
border.color: Theme.alpha(Theme.borderSoft, 0.7)
ListView {
id: fileList
anchors.fill: parent
anchors.margins: Theme.spacingXs
clip: true
model: logsPage.model
spacing: 2
boundsBehavior: Flickable.StopAtBounds
currentIndex: -1
ScrollBar.vertical: ThemedScrollBar {
}
delegate: Rectangle {
id: logRow
required property string name
required property string modified
required property string modifiedCompact
required property string sizeText
width: fileList.width
height: 42
radius: Theme.radiusMd
color: logsPage.selectedName === name ? Theme.alpha(Theme.accent, Theme.dark ? 0.2 : 0.12) : rowMouse.containsMouse ? Theme.alpha(Theme.bgRaised, 0.76) : "transparent"
border.width: logsPage.selectedName === name ? 1 : 0
border.color: Theme.alpha(Theme.accent, 0.42)
RowLayout {
anchors.fill: parent
anchors.leftMargin: Theme.spacingMd
anchors.rightMargin: Theme.spacingSm
spacing: Theme.spacingMd
Text {
text: logRow.name
color: Theme.textPrimary
font.pixelSize: Theme.fontBody
font.weight: Font.Medium
elide: Text.ElideRight
Layout.fillWidth: true
}
Text {
text: logRow.sizeText + " · " + logRow.modifiedCompact
color: Theme.textMuted
font.pixelSize: Theme.fontSmall
}
ThemedButton {
implicitWidth: 74
text: qsTr("Save…")
enabled: !logsPage.saving
Accessible.name: qsTr("Save %1").arg(logRow.name)
onClicked: logsPage.saveLog(logRow.name)
}
}
MouseArea {
id: rowMouse
anchors.fill: parent
anchors.rightMargin: 80
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
Accessible.role: Accessible.ListItem
Accessible.name: logRow.name
onClicked: logsPage.selectLog(logRow.name)
}
}
}
EmptyState {
anchors.centerIn: parent
visible: logsPage.hasLoaded && !logsPage.loading && logsPage.totalCount === 0
text: qsTr("No log files found")
hint: qsTr("The server did not report any downloadable logs.")
}
}
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
radius: Theme.radiusLg
color: Theme.alpha(Theme.bgSurface, Theme.dark ? 0.64 : 0.94)
border.width: 1
border.color: Theme.alpha(Theme.borderSoft, 0.7)
ColumnLayout {
anchors.fill: parent
spacing: 0
RowLayout {
Layout.fillWidth: true
Layout.leftMargin: Theme.spacingLg
Layout.rightMargin: Theme.spacingMd
Layout.topMargin: Theme.spacingMd
Layout.bottomMargin: Theme.spacingMd
spacing: Theme.spacingMd
ColumnLayout {
Layout.fillWidth: true
spacing: Theme.spacingXs
Text {
text: logsPage.selectedName.length > 0 ? logsPage.selectedName : qsTr("No log file selected")
color: Theme.textPrimary
font.pixelSize: Theme.fontHeading
font.weight: Font.DemiBold
elide: Text.ElideMiddle
Layout.fillWidth: true
}
Text {
text: logsPage.selectedMeta
color: Theme.textMuted
font.pixelSize: Theme.fontSmall
elide: Text.ElideRight
Layout.fillWidth: true
}
}
ThemedButton {
visible: logsPage.selectedName.length > 0
text: logsPage.saving ? qsTr("Saving…") : qsTr("Save as…")
enabled: !logsPage.saving && !logsPage.contentLoading
onClicked: logsPage.saveLog(logsPage.selectedName)
}
}
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 1
color: Theme.borderSoft
}
ScrollView {
id: contentScroll
visible: logsPage.selectedName.length > 0 && !logsPage.contentLoading && logsPage.contentError.length === 0
Layout.fillWidth: true
Layout.fillHeight: true
clip: true
TextArea {
width: contentScroll.availableWidth
text: logsPage.content
readOnly: true
selectByMouse: true
wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere
color: Theme.textBody
selectionColor: Theme.accent
selectedTextColor: "white"
font.family: "monospace"
font.pixelSize: Theme.fontSmall
padding: Theme.spacingLg
Accessible.name: qsTr("Log contents")
background: Rectangle {
color: "transparent"
}
}
ScrollBar.vertical: ThemedScrollBar {
}
}
LoadingState {
visible: logsPage.contentLoading
Layout.fillWidth: true
Layout.fillHeight: true
title: qsTr("Loading log")
message: logsPage.selectedName
}
ErrorState {
visible: !logsPage.contentLoading && logsPage.contentError.length > 0
Layout.fillWidth: true
Layout.fillHeight: true
title: qsTr("Could not load log")
message: logsPage.contentError
retryText: qsTr("Try again")
onRetryRequested: logsPage.selectLog(logsPage.selectedName)
}
EmptyState {
visible: logsPage.selectedName.length === 0 && logsPage.totalCount > 0
Layout.fillWidth: true
Layout.fillHeight: true
text: qsTr("Select a log file")
hint: qsTr("Choose a file above to inspect its contents.")
}
}
}
}
LoadingState {
anchors.fill: parent
visible: logsPage.loading && !logsPage.hasLoaded
title: qsTr("Loading logs")
message: qsTr("Fetching available server log files…")
}
ErrorState {
anchors.fill: parent
visible: !logsPage.loading && !logsPage.hasLoaded && logsPage.errorMessage.length > 0
title: qsTr("Could not load logs")
message: logsPage.errorMessage
onRetryRequested: logsPage.refresh()
}
ToastHost {
id: toastHost
anchors.fill: parent
}
Connections {
function onToastRequested(message, kind) {
toastHost.show(message, kind);
}
target: logsPage
}
}