diff options
Diffstat (limited to 'modules/utilities')
| -rw-r--r-- | modules/utilities/Content.qml | 6 | ||||
| -rw-r--r-- | modules/utilities/Wrapper.qml | 7 | ||||
| -rw-r--r-- | modules/utilities/cards/IdleInhibit.qml | 5 | ||||
| -rw-r--r-- | modules/utilities/cards/Record.qml | 315 | ||||
| -rw-r--r-- | modules/utilities/cards/Toggles.qml | 2 |
5 files changed, 333 insertions, 2 deletions
diff --git a/modules/utilities/Content.qml b/modules/utilities/Content.qml index bcac9b8..3e29402 100644 --- a/modules/utilities/Content.qml +++ b/modules/utilities/Content.qml @@ -5,12 +5,18 @@ import QtQuick.Layouts ColumnLayout { id: root + required property var props required property var visibilities spacing: Appearance.spacing.normal IdleInhibit {} + Record { + props: root.props + visibilities: root.visibilities + } + Toggles { visibilities: root.visibilities } diff --git a/modules/utilities/Wrapper.qml b/modules/utilities/Wrapper.qml index 830ee5a..4de4014 100644 --- a/modules/utilities/Wrapper.qml +++ b/modules/utilities/Wrapper.qml @@ -2,12 +2,18 @@ pragma ComponentBehavior: Bound import qs.components import qs.config +import Quickshell import QtQuick Item { id: root required property var visibilities + readonly property PersistentProperties props: PersistentProperties { + property bool recordingListExpanded: false + + reloadableId: "utilities" + } visible: height > 0 implicitHeight: 0 @@ -76,6 +82,7 @@ Item { active: true sourceComponent: Content { + props: root.props visibilities: root.visibilities } } diff --git a/modules/utilities/cards/IdleInhibit.qml b/modules/utilities/cards/IdleInhibit.qml index 87f6211..4e95a56 100644 --- a/modules/utilities/cards/IdleInhibit.qml +++ b/modules/utilities/cards/IdleInhibit.qml @@ -117,6 +117,9 @@ StyledRect { } Behavior on implicitHeight { - Anim {} + Anim { + duration: Appearance.anim.durations.expressiveDefaultSpatial + easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial + } } } diff --git a/modules/utilities/cards/Record.qml b/modules/utilities/cards/Record.qml new file mode 100644 index 0000000..c993785 --- /dev/null +++ b/modules/utilities/cards/Record.qml @@ -0,0 +1,315 @@ +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 + +StyledRect { + id: root + + required property var props + required property var visibilities + + Layout.fillWidth: true + implicitHeight: layout.implicitHeight + layout.anchors.margins * 2 + + radius: Appearance.rounding.normal + color: Colours.palette.m3surfaceContainer + + ColumnLayout { + id: layout + + anchors.fill: parent + anchors.margins: Appearance.padding.large + spacing: Appearance.spacing.normal + + RowLayout { + spacing: Appearance.spacing.normal + + StyledRect { + implicitWidth: implicitHeight + implicitHeight: { + const h = icon.implicitHeight + Appearance.padding.smaller * 2; + return h - (h % 2); + } + + radius: Appearance.rounding.full + color: Recorder.running ? Colours.palette.m3secondary : Colours.palette.m3secondaryContainer + + MaterialIcon { + id: icon + + anchors.centerIn: parent + anchors.horizontalCenterOffset: -0.5 + anchors.verticalCenterOffset: 1.5 + text: "screen_record" + color: Recorder.running ? Colours.palette.m3onSecondary : Colours.palette.m3onSecondaryContainer + font.pointSize: Appearance.font.size.large + } + } + + ColumnLayout { + Layout.fillWidth: true + spacing: 0 + + StyledText { + Layout.fillWidth: true + text: qsTr("Screen Recorder") + font.pointSize: Appearance.font.size.normal + elide: Text.ElideRight + } + + StyledText { + Layout.fillWidth: true + text: Recorder.paused ? qsTr("Recording paused") : Recorder.running ? qsTr("Recording running") : qsTr("Recording off") + color: Colours.palette.m3onSurfaceVariant + font.pointSize: Appearance.font.size.small + elide: Text.ElideRight + } + } + + IconButton { + icon: "not_started" + checked: Recorder.running + type: IconButton.Filled + inactiveColour: Colours.palette.m3surfaceContainerHighest + toggle: true + + function onClicked(): void { + Recorder.toggle(); + } + } + } + + WrapperMouseArea { + Layout.bottomMargin: -layout.spacing + 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 + + function onClicked(): void { + root.props.recordingListExpanded = !root.props.recordingListExpanded; + } + } + } + } + + StyledListView { + id: recordingList + + 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: recordingList.contentItem.left + anchors.right: recordingList.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 + + function onClicked(): void { + root.visibilities.utilities = false; + Quickshell.execDetached([...Config.general.apps.playback, recording.modelData.path]); + } + } + + IconButton { + icon: "folder" + type: IconButton.Text + + function onClicked(): void { + root.visibilities.utilities = false; + Quickshell.execDetached([...Config.general.apps.explorer, recording.modelData.path]); + } + } + + IconButton { + icon: "delete_forever" + type: IconButton.Text + label.color: Colours.palette.m3error + + function onClicked(): void { + CUtils.deleteFile(Qt.resolvedUrl(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: recordingList.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 + } + } + } + } +} diff --git a/modules/utilities/cards/Toggles.qml b/modules/utilities/cards/Toggles.qml index 8e4dfb5..6e2d227 100644 --- a/modules/utilities/cards/Toggles.qml +++ b/modules/utilities/cards/Toggles.qml @@ -89,7 +89,7 @@ StyledRect { required property string icon required property string text property bool checked - property bool toggle + property bool toggle: true property bool internalChecked function onClicked(): void { |