diff options
Diffstat (limited to 'modules/utilities/cards/RecordingList.qml')
| -rw-r--r-- | modules/utilities/cards/RecordingList.qml | 237 |
1 files changed, 237 insertions, 0 deletions
diff --git a/modules/utilities/cards/RecordingList.qml b/modules/utilities/cards/RecordingList.qml new file mode 100644 index 0000000..1250ab3 --- /dev/null +++ b/modules/utilities/cards/RecordingList.qml @@ -0,0 +1,237 @@ +pragma ComponentBehavior: Bound + +import qs.components +import qs.components.controls +import qs.components.containers +import qs.services +import qs.config +import qs.utils +import Caelestia +import Caelestia.Models +import Quickshell +import Quickshell.Widgets +import QtQuick +import QtQuick.Layouts + +ColumnLayout { + id: root + + required property var props + required property var visibilities + + spacing: 0 + + WrapperMouseArea { + Layout.fillWidth: true + + cursorShape: Qt.PointingHandCursor + onClicked: root.props.recordingListExpanded = !root.props.recordingListExpanded + + RowLayout { + spacing: Appearance.spacing.smaller + + MaterialIcon { + Layout.alignment: Qt.AlignVCenter + text: "list" + font.pointSize: Appearance.font.size.large + } + + StyledText { + Layout.alignment: Qt.AlignVCenter + Layout.fillWidth: true + text: qsTr("Recordings") + font.pointSize: Appearance.font.size.normal + } + + IconButton { + icon: root.props.recordingListExpanded ? "unfold_less" : "unfold_more" + type: IconButton.Text + label.animate: true + onClicked: root.props.recordingListExpanded = !root.props.recordingListExpanded + } + } + } + + StyledListView { + id: list + + model: FileSystemModel { + path: Paths.recsdir + nameFilters: ["recording_*.mp4"] + sortReverse: true + } + + Layout.fillWidth: true + Layout.rightMargin: -Appearance.spacing.small + implicitHeight: (Appearance.font.size.larger + Appearance.padding.small) * (root.props.recordingListExpanded ? 10 : 3) + clip: true + + StyledScrollBar.vertical: StyledScrollBar {} + + delegate: RowLayout { + id: recording + + required property FileSystemEntry modelData + property string baseName + + anchors.left: list.contentItem.left + anchors.right: list.contentItem.right + anchors.rightMargin: Appearance.spacing.small + spacing: Appearance.spacing.small / 2 + + Component.onCompleted: baseName = modelData.baseName + + StyledText { + Layout.fillWidth: true + Layout.rightMargin: Appearance.spacing.small / 2 + text: { + const time = recording.baseName; + const matches = time.match(/^recording_(\d{4})(\d{2})(\d{2})_(\d{2})-(\d{2})-(\d{2})/); + if (!matches) + return time; + const date = new Date(...matches.slice(1)); + return qsTr("Recording at %1").arg(Qt.formatDateTime(date, Qt.locale())); + } + color: Colours.palette.m3onSurfaceVariant + elide: Text.ElideRight + } + + IconButton { + icon: "play_arrow" + type: IconButton.Text + onClicked: { + root.visibilities.utilities = false; + Quickshell.execDetached(["app2unit", "--", ...Config.general.apps.playback, recording.modelData.path]); + } + } + + IconButton { + icon: "folder" + type: IconButton.Text + onClicked: { + root.visibilities.utilities = false; + Quickshell.execDetached(["app2unit", "--", ...Config.general.apps.explorer, recording.modelData.path]); + } + } + + IconButton { + icon: "delete_forever" + type: IconButton.Text + label.color: Colours.palette.m3error + stateLayer.color: Colours.palette.m3error + onClicked: root.props.recordingConfirmDelete = recording.modelData.path + } + } + + add: Transition { + Anim { + property: "opacity" + from: 0 + to: 1 + } + Anim { + property: "scale" + from: 0.5 + to: 1 + } + } + + remove: Transition { + Anim { + property: "opacity" + to: 0 + } + Anim { + property: "scale" + to: 0.5 + } + } + + displaced: Transition { + Anim { + properties: "opacity,scale" + to: 1 + } + Anim { + property: "y" + } + } + + Loader { + anchors.centerIn: parent + + opacity: list.count === 0 ? 1 : 0 + active: opacity > 0 + asynchronous: true + + sourceComponent: ColumnLayout { + spacing: Appearance.spacing.small + + MaterialIcon { + Layout.alignment: Qt.AlignHCenter + text: "scan_delete" + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.extraLarge + + opacity: root.props.recordingListExpanded ? 1 : 0 + scale: root.props.recordingListExpanded ? 1 : 0 + Layout.preferredHeight: root.props.recordingListExpanded ? implicitHeight : 0 + + Behavior on opacity { + Anim {} + } + + Behavior on scale { + Anim {} + } + + Behavior on Layout.preferredHeight { + Anim {} + } + } + + RowLayout { + spacing: Appearance.spacing.smaller + + MaterialIcon { + Layout.alignment: Qt.AlignHCenter + text: "scan_delete" + color: Colours.palette.m3outline + + opacity: !root.props.recordingListExpanded ? 1 : 0 + scale: !root.props.recordingListExpanded ? 1 : 0 + Layout.preferredWidth: !root.props.recordingListExpanded ? implicitWidth : 0 + + Behavior on opacity { + Anim {} + } + + Behavior on scale { + Anim {} + } + + Behavior on Layout.preferredWidth { + Anim {} + } + } + + StyledText { + text: qsTr("No recordings found") + color: Colours.palette.m3outline + } + } + } + + Behavior on opacity { + Anim {} + } + } + + Behavior on implicitHeight { + Anim { + duration: Appearance.anim.durations.expressiveDefaultSpatial + easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial + } + } + } +} |