338 lines
11 KiB
QML
338 lines
11 KiB
QML
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
|
|
property bool modePickerExpanded
|
|
|
|
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
|
|
z: 1
|
|
|
|
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
|
|
}
|
|
}
|
|
|
|
SplitButton {
|
|
active: menuItems.find(m => root.props.recordingMode === m.icon + m.text) ?? menuItems[0]
|
|
menu.onItemSelected: item => root.props.recordingMode = item.icon + item.text
|
|
|
|
menuItems: [
|
|
MenuItem {
|
|
icon: "fullscreen"
|
|
text: qsTr("Record fullscreen")
|
|
activeText: qsTr("Fullscreen")
|
|
onClicked: Recorder.toggle()
|
|
},
|
|
MenuItem {
|
|
icon: "screenshot_region"
|
|
text: qsTr("Record region")
|
|
activeText: qsTr("Region")
|
|
onClicked: Recorder.toggle(["-r"])
|
|
},
|
|
MenuItem {
|
|
icon: "select_to_speak"
|
|
text: qsTr("Record fullscreen with sound")
|
|
activeText: qsTr("Fullscreen")
|
|
onClicked: Recorder.toggle(["-s"])
|
|
},
|
|
MenuItem {
|
|
icon: "volume_up"
|
|
text: qsTr("Record region with sound")
|
|
activeText: qsTr("Region")
|
|
onClicked: Recorder.toggle(["-sr"])
|
|
}
|
|
]
|
|
}
|
|
}
|
|
|
|
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(["app2unit", "--", ...Config.general.apps.playback, recording.modelData.path]);
|
|
}
|
|
}
|
|
|
|
IconButton {
|
|
icon: "folder"
|
|
type: IconButton.Text
|
|
|
|
function onClicked(): void {
|
|
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
|
|
|
|
function onClicked(): void {
|
|
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: 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
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|