summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
author2 * r + 2 * t <61896496+soramanew@users.noreply.github.com>2025-09-14 16:59:19 +1000
committer2 * r + 2 * t <61896496+soramanew@users.noreply.github.com>2025-09-14 16:59:19 +1000
commit4b221e2fd5b7faf4747cff6c590b6ddc38870901 (patch)
tree3041635aa0e94a7783410ccf76424143060fa605 /modules
parentinternal: close panels when fullscreen app (diff)
downloadcaelestia-shell-4b221e2fd5b7faf4747cff6c590b6ddc38870901.tar.gz
caelestia-shell-4b221e2fd5b7faf4747cff6c590b6ddc38870901.tar.bz2
caelestia-shell-4b221e2fd5b7faf4747cff6c590b6ddc38870901.zip
utilities: add recording control
Diffstat (limited to 'modules')
-rw-r--r--modules/utilities/Content.qml6
-rw-r--r--modules/utilities/Wrapper.qml7
-rw-r--r--modules/utilities/cards/IdleInhibit.qml5
-rw-r--r--modules/utilities/cards/Record.qml315
-rw-r--r--modules/utilities/cards/Toggles.qml2
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 {