Warning, /plasma-mobile/plasma-camera/src/contents/ui/CameraPage.qml is written in an unsupported language. File is not indexed.

0001 // SPDX-FileCopyrightText: 2018 Jonah BrĂ¼chert <jbb@kaidan.im>
0002 // SPDX-FileCopyrightText: 2013 Digia Plc and/or its subsidiary(-ies)
0003 // SPDX-License-Identifier: BSD-3-Clause
0004 
0005 import QtQuick 2.7
0006 import QtMultimedia 5.8
0007 import org.kde.kirigami 2.19 as Kirigami
0008 import QtQuick.Controls 2.12 as Controls
0009 import QtQuick.Controls.Material 2.0
0010 import QtQuick.Layouts 1.2
0011 import QtGraphicalEffects 1.0
0012 import "./components" as Components
0013 
0014 Kirigami.Page {
0015     id: cameraPage
0016 
0017     property var camera
0018 
0019     title: i18n("Camera")
0020 
0021     leftPadding: 0
0022     rightPadding: 0
0023     bottomPadding: 0
0024     topPadding: 0
0025 
0026     globalToolBarStyle: Kirigami.Settings.isMobile ? Kirigami.ApplicationHeaderStyle.None : Kirigami.ApplicationHeaderStyle.ToolBar
0027     onIsCurrentPageChanged: isCurrentPage && pageStack.depth > 1 && pageStack.pop()
0028     FontMetrics {
0029         id: fontMetrics
0030     }
0031 
0032     function formatText(count, modelData) {
0033         var data = count === 12 ? modelData + 1 : modelData;
0034         return  data + " s";
0035     }
0036     leftAction: Kirigami.Action {
0037         id: switchModeAction
0038         visible: false
0039         text: i18n("Switch mode")
0040         icon.color: "transparent"
0041         icon.name: {
0042             if (camera.captureMode === Camera.CaptureStillImage)
0043                 return "emblem-videos-symbolic"
0044             else if (camera.captureMode === Camera.CaptureVideo)
0045                 return "camera-photo-symbolic"
0046         }
0047         enabled: (camera.videoRecorder.recorderStatus !== CameraRecorder.RecordingStatus)
0048         onTriggered: {
0049             if (camera.captureMode === Camera.CaptureStillImage)
0050                 camera.captureMode = Camera.CaptureVideo
0051             else
0052                 camera.captureMode = Camera.CaptureStillImage
0053 
0054             console.log("Capture Mode switched")
0055         }
0056     }
0057     mainAction: Kirigami.Action {
0058         id: captureAction
0059         visible: false
0060         text: {
0061             if (selfTimer.running)
0062                 return i18n("Cancel self-timer")
0063             else if (camera.captureMode === Camera.CaptureStillImage)
0064                 return i18n("Capture photo")
0065             else if (camera.videoRecorder.recorderStatus === CameraRecorder.RecordingStatus)
0066                 return i18n("Stop recording video")
0067             else if (camera.captureMode === Camera.CaptureVideo)
0068                 return i18n("Start recording video")
0069         }
0070         icon.color: "transparent"
0071         icon.name: {
0072             if (selfTimer.running)
0073                 return "dialog-error-symbolic"
0074             else if (camera.captureMode === Camera.CaptureStillImage)
0075                 return "camera-photo-symbolic"
0076             else if (camera.videoRecorder.recorderStatus === CameraRecorder.RecordingStatus)
0077                 return "media-playback-stop"
0078             else if (camera.captureMode === Camera.CaptureVideo)
0079                 return "emblem-videos-symbolic"
0080         }
0081         onTriggered: {
0082             if (selfTimer.running) {
0083                 selfTimer.stop()
0084             }
0085             else if ((camera.selfTimerDuration === 0) || (camera.videoRecorder.recorderStatus === CameraRecorder.RecordingStatus)) {
0086                 selfTimer.triggered()
0087             }
0088             else {
0089                 countdownTimer.remainingSeconds = camera.selfTimerDuration
0090                 countdownTimer.start()
0091                 selfTimer.start()
0092             }
0093         }
0094         enabled: {
0095             if ((camera.captureMode === camera.CaptureStillImage) && !selfTimer.running)
0096                 return camera.imageCapture.ready
0097             else
0098                 return true
0099         }
0100     }
0101     rightAction: Kirigami.Action {
0102         id: switchCameraAction
0103         visible: false
0104         text: i18n("Switch Camera")
0105         icon.color: "transparent"
0106         icon.name: "camera-photo-symbolic"
0107         enabled: (camera.position !== Camera.UnspecifiedPosition)
0108         onTriggered: {
0109             if (CameraSettings.cameraPosition === Camera.BackFace)
0110                 CameraSettings.cameraPosition = Camera.FrontFace
0111             else if (CameraSettings.cameraPosition === Camera.FrontFace)
0112                 CameraSettings.cameraPosition = Camera.BackFace
0113         }
0114     }
0115 
0116     Rectangle {
0117             id: cameraUI
0118             state: "PhotoCapture"
0119             anchors.fill: parent
0120 
0121             color: "black"
0122 
0123             states: [
0124                 State {
0125                     name: "PhotoCapture"
0126                     StateChangeScript {
0127                         script: {
0128                             cameraPage.camera.captureMode = Camera.CaptureStillImage
0129                             cameraPage.camera.start()
0130                         }
0131                     }
0132                 },
0133                 State {
0134                     name: "VideoCapture"
0135                     StateChangeScript {
0136                         script: {
0137                             cameraPage.camera.captureMode = Camera.CaptureVideo
0138                             cameraPage.camera.start()
0139                         }
0140                     }
0141 
0142                 }
0143             ]
0144 
0145             Kirigami.Heading {
0146                 anchors.centerIn: parent
0147                 wrapMode: Text.WordWrap
0148                 text: {
0149                     if (cameraPage.camera.availability === Camera.Unavailable)
0150                         return i18n("Camera not available")
0151                     else if (cameraPage.camera.availability === Camera.Busy)
0152                         return i18n("Camera is busy. Is another application using it?")
0153                     else if (cameraPage.camera.availability === Camera.ResourceMissing)
0154                         return i18n("Missing camera resource.")
0155                     else if (cameraPage.camera.availability === Camera.Available)
0156                         return ""
0157                 }
0158             }
0159 
0160             VideoOutput {
0161                 id: viewfinder
0162                 visible: cameraUI.state == "PhotoCapture" || cameraUI.state == "VideoCapture"
0163 
0164                 // Workaround
0165                 orientation: Kirigami.Settings.isMobile ? -90 : 0
0166                 width: parent.width
0167                 height: parent.height-controlContainer.height
0168 
0169                 source: cameraPage.camera
0170             }
0171 
0172             PinchArea {
0173                 anchors.fill: parent
0174                 property real initialZoom
0175                 onPinchStarted: {
0176                     initialZoom = cameraPage.camera.digitalZoom;
0177                 }
0178                 onPinchUpdated: {
0179                     var scale = cameraPage.camera.maximumDigitalZoom / 8 * pinch.scale - cameraPage.camera.maximumDigitalZoom / 8;
0180                     cameraPage.camera.setDigitalZoom(Math.min(cameraPage.camera.maximumDigitalZoom, cameraPage.camera.digitalZoom + scale))
0181                 }
0182             }
0183 
0184             MouseArea {
0185                 anchors.fill: parent
0186 
0187                 onClicked: {
0188                     if (cameraPage.camera.lockStatus === cameraPage.camera.Unlocked) {
0189                         cameraPage.camera.searchAndLock();
0190                         console.log("searching focus...")
0191                     }
0192                     else {
0193                         cameraPage.camera.unlock();
0194                         console.log("unlocking focus...")
0195                     }
0196                 }
0197             }
0198         }
0199 
0200     Rectangle{
0201         id: controlContainer
0202 
0203         anchors.bottom: cameraUI.bottom
0204         height: controlsLayout.height
0205         width: cameraPage.width
0206         color: "black"
0207         ColumnLayout {
0208             id: controlsLayout
0209             width: parent.width
0210 
0211             Item {
0212                 id: timerDuration
0213 
0214                 Layout.margins: Kirigami.Units.largeSpacing
0215                 clip: true
0216                 Layout.minimumHeight: timerButton.checked ? Kirigami.Units.gridUnit * 2 : 0
0217                 Layout.fillWidth: true
0218                 Behavior on Layout.minimumHeight {
0219                     PropertyAnimation {
0220                         duration: Kirigami.Units.shortDuration
0221                         easing.type: Easing.InOutCubic
0222                     }
0223                 }
0224                 Component {
0225                     id: delegateComponent
0226                     Item{
0227                         width: Kirigami.Units.gridUnit *2
0228                         opacity: 1.0 - Math.abs(Controls.Tumbler.displacement) / (Controls.Tumbler.tumbler.visibleItemCount / 2.5)
0229                         Controls.Label {
0230                             anchors.centerIn:parent
0231                             rotation: 90
0232                             color: "white"
0233                             text: formatText(Controls.Tumbler.tumbler.count, modelData)
0234                             font.pixelSize: fontMetrics.font.pixelSize * 1.25
0235                         }
0236                     }
0237                 }
0238 
0239                 Controls.Tumbler{
0240                     id: timerTumbler
0241 
0242                     anchors.centerIn: parent
0243                     wrap: false
0244                     implicitHeight: Kirigami.Units.gridUnit * 25
0245                     Layout.alignment: Qt.AlignHCenter
0246                     model: [0,2,5,10,20]
0247                     onMovingChanged: {
0248                         camera.selfTimerDuration = model[currentIndex]
0249                     }
0250                     rotation:-90
0251                     delegate: delegateComponent
0252                     visibleItemCount: 7
0253 
0254                 }
0255             }
0256 
0257             RowLayout{
0258                 spacing: Kirigami.Units.gridUnit
0259                 Layout.fillWidth: true
0260                 Layout.alignment: Qt.AlignHCenter
0261                 Item {
0262                     Layout.fillWidth: true
0263                 }
0264                 Item{
0265                     width: 40
0266                     height:40
0267                     PreviewArea {
0268                         id: previewArea
0269                         imageCapture: camera.imageCapture
0270                         videoRecorder: camera.videoRecorder
0271                         anchors.fill: parent
0272 
0273                     }
0274                     Kirigami.Icon {
0275                         visible: !previewArea.visible
0276                         anchors.centerIn: parent
0277                         source: "photo"
0278                         color: "white"
0279                         height: Kirigami.Units.gridUnit *1.3
0280                     }
0281                 }
0282                 Controls.ToolButton{
0283                     id: timerButton
0284                     checkable: true
0285                     icon.name: "clock"
0286                     Layout.fillWidth: true
0287                     icon.color: "white"
0288                     Layout.maximumWidth: height
0289 
0290                 }
0291                 Controls.ToolButton{
0292                     Layout.fillWidth: true
0293                     implicitHeight: Kirigami.Units.gridUnit * 4
0294                     Layout.maximumWidth: height
0295 
0296                     onClicked: captureAction.triggered()
0297 
0298                     background: Rectangle {
0299                         height: parent.height
0300                         width: height
0301                         radius: height/2
0302                         color: modeSelector.selectedIndex === 0 ?
0303                                    Kirigami.ColorUtils.linearInterpolation(
0304                                    Kirigami.Theme.hoverColor,
0305                                    "transparent", 0.6) :
0306                                    Kirigami.ColorUtils.linearInterpolation(
0307                                    "red",
0308                                    "transparent", 0.6)
0309                         border.color: "white"
0310                         border.width: parent.hovered ? (parent.down ? parent.height/2: 10 ) :  5
0311                         Rectangle {
0312                             opacity: modeSelector.selectedIndex === 1 ? 1 : 0
0313                             height: modeSelector.selectedIndex === 1 ? Kirigami.Units.gridUnit : 0
0314                             width: height
0315                             anchors.centerIn: parent
0316                             radius: (camera.videoRecorder.recorderStatus === CameraRecorder.RecordingStatus) ? Kirigami.Units.smallSpacing : height/2
0317                             Behavior on opacity {
0318                                 PropertyAnimation {
0319                                     duration: Kirigami.Units.shortDuration
0320                                     easing.type: Easing.InOutCubic
0321                                 }
0322                             }
0323                             Behavior on height {
0324                                 PropertyAnimation {
0325                                     duration: Kirigami.Units.shortDuration
0326                                     easing.type: Easing.InOutCubic
0327                                 }
0328                             }
0329                         }
0330                         Behavior on border.width {
0331                             PropertyAnimation {
0332                                 duration: Kirigami.Units.shortDuration
0333                                 easing.type: Easing.InOutCubic
0334                             }
0335                         }
0336                         Behavior on color {
0337                             PropertyAnimation {
0338                                 duration: Kirigami.Units.longDuration
0339                                 easing.type: Easing.InOutCubic
0340                             }
0341                         }
0342                     }
0343 
0344                 }
0345                 Controls.ToolButton{
0346                     Layout.fillWidth: true
0347                     icon.name: "circular-arrow-shape"
0348                     icon.color: "white"
0349                     Layout.maximumWidth: height
0350                     onClicked: switchCameraAction.triggered()
0351                     enabled: switchCameraAction.enabled
0352                 }
0353                 Item{
0354                     width: 40
0355                     height:40
0356                     Controls.ToolButton{
0357                         id: settingsButton
0358                         icon.name: "settings-configure"
0359                         icon.color: "white"
0360                         anchors.centerIn: parent
0361                         onClicked: applicationWindow().globalDrawer.open()
0362                     }
0363                 }
0364                 Item {
0365                     Layout.fillWidth: true
0366                 }
0367             }
0368             Components.RadioSelector {
0369                 id: modeSelector
0370                 color: "white"
0371                 Layout.alignment: Qt.AlignHCenter
0372                 Layout.fillWidth: true
0373                 Layout.margins: Kirigami.Units.largeSpacing * 3
0374                 Layout.maximumWidth: Kirigami.Units.gridUnit * 10
0375                 consistentWidth: true
0376                 actions: [
0377                     Kirigami.Action {
0378                         text: i18n("Photo")
0379                         icon.name: "camera-photo-symbolic"
0380                         onTriggered: camera.captureMode = Camera.CaptureStillImage
0381                     },
0382                     Kirigami.Action {
0383                         text: i18n("Video")
0384                         icon.name: "emblem-videos-symbolic"
0385                         onTriggered: camera.captureMode = Camera.CaptureVideo
0386                     }
0387                 ]
0388             }
0389         }
0390     }
0391 
0392     ZoomControl {
0393         anchors {
0394             right: parent.right
0395             top: parent.top
0396             margins: Kirigami.Units.gridUnit * 2
0397         }
0398         width : Kirigami.Units.gridUnit * 2
0399         height: parent.height - controlContainer.height
0400         currentZoom: cameraPage.camera.digitalZoom
0401         maximumZoom: Math.min(4.0, cameraPage.camera.maximumDigitalZoom)
0402         onZoomTo: cameraPage.camera.setDigitalZoom(value)
0403     }
0404 
0405     Timer { // counts the seconds from the beginning of the current video recording
0406         id: recordingDurationTimer
0407         interval: 1000
0408         running: camera.videoRecorder.recorderStatus === CameraRecorder.RecordingStatus
0409         repeat: true
0410         property int recordingDurationSeconds: 0
0411 
0412         onTriggered: {
0413             recordingDurationSeconds++
0414         }
0415 
0416         onRunningChanged: {
0417             if (!running) {
0418                 recordingDurationSeconds = 0
0419             }
0420         }
0421     }
0422 
0423     RowLayout {
0424         id: recordingFeedback
0425         visible: (camera.videoRecorder.recorderStatus === CameraRecorder.RecordingStatus)
0426         spacing: Kirigami.Units.gridUnit
0427 
0428         anchors {
0429             left: parent.left
0430             top: parent.top
0431             margins: Kirigami.Units.gridUnit * 2
0432         }
0433 
0434         Rectangle {
0435             color: "red"
0436             radius: Kirigami.Units.gridUnit
0437             height: Kirigami.Units.gridUnit * 2
0438             width: height
0439         }
0440 
0441         Text {
0442             text: {
0443                 "%1%2:%3".arg(
0444                     (Math.trunc(recordingDurationTimer.recordingDurationSeconds / 60) > 59) ? // display hour count only on demand
0445                     (Math.trunc(Math.trunc(recordingDurationTimer.recordingDurationSeconds / 60) / 60) + ":") :
0446                     ""
0447                 )
0448                 .arg(
0449                     (((Math.trunc(recordingDurationTimer.recordingDurationSeconds / 60) % 60) < 10) ? "0" : "") + // zero padding
0450                     (Math.trunc(recordingDurationTimer.recordingDurationSeconds / 60) % 60)
0451                 )
0452                 .arg(
0453                     (((recordingDurationTimer.recordingDurationSeconds % 60) < 10) ? "0" : "") + // zero padding
0454                     (recordingDurationTimer.recordingDurationSeconds % 60)
0455                 )
0456             }
0457             font.pixelSize: Kirigami.Units.gridUnit
0458             color: "white"
0459         }
0460 
0461         layer.enabled: recordingFeedback.enabled
0462         layer.effect: DropShadow {
0463             color: Material.dropShadowColor
0464             samples: 30
0465             spread: 0.5
0466         }
0467     }
0468 
0469     Timer {
0470         id: selfTimer
0471         interval: camera.selfTimerDuration * 1000
0472         running: false
0473         repeat: false
0474 
0475         onTriggered: {
0476             running = false
0477 
0478             if (camera.captureMode === Camera.CaptureStillImage) {
0479                 if (camera.imageCapture.ready) {
0480                     camera.imageCapture.capture()
0481                     previewArea.setPhotoPreview()
0482                     showPassiveNotification(i18n("Took a photo"))
0483                 }
0484                 else {
0485                     showPassiveNotification(i18n("Failed to take a photo"))
0486                 }
0487             }
0488             else if (camera.videoRecorder.recorderStatus === CameraRecorder.RecordingStatus) {
0489                 camera.videoRecorder.stop()
0490                 previewArea.setVideoPreview()
0491                 showPassiveNotification(i18n("Stopped recording"))
0492             }
0493             else if (camera.captureMode === Camera.CaptureVideo) {
0494                 camera.videoRecorder.record()
0495 
0496                 if (camera.videoRecorder.recorderStatus === CameraRecorder.RecordingStatus) {
0497                     showPassiveNotification(i18n("Started recording"))
0498                 }
0499                 else {
0500                     showPassiveNotification(i18n("Failed to start recording"))
0501                 }
0502             }
0503         }
0504 
0505         onRunningChanged: {
0506             if (!running) {
0507                 camera.selfTimerRunning = false
0508                 selfTimerAnimation.stop()
0509                 countdownTimer.stop()
0510                 countdownTimer.remainingSeconds = camera.selfTimerDuration
0511                 selfTimerIcon.opacity = 1
0512             }
0513             else {
0514                 camera.selfTimerRunning = true
0515             }
0516         }
0517     }
0518 
0519     Timer { // counts the remaining seconds until the selfTimer invokes the capture action
0520         id: countdownTimer
0521         interval: 1000
0522         running: false
0523         repeat: true
0524         property int remainingSeconds: 0
0525 
0526         onTriggered: {
0527             remainingSeconds--
0528         }
0529     }
0530 
0531     RowLayout {
0532         id: selfTimerInfo
0533         visible: !(camera.selfTimerDuration === 0) && !((camera.captureMode === Camera.CaptureVideo) && (camera.videoRecorder.recorderStatus === CameraRecorder.RecordingStatus))
0534 
0535         anchors {
0536             top: parent.top
0537             horizontalCenter: parent.horizontalCenter
0538             margins: Kirigami.Units.gridUnit * 1
0539         }
0540 
0541         Kirigami.Icon {
0542             id: selfTimerIcon
0543             source: "alarm-symbolic"
0544             color: selfTimer.running ? "red" : "white"
0545             Layout.preferredWidth: Kirigami.Units.gridUnit
0546             Layout.preferredHeight: Kirigami.Units.gridUnit
0547             Layout.fillWidth: true
0548             Layout.fillHeight: true
0549             Layout.alignment: Qt.AlignCenter
0550         }
0551 
0552         Text {
0553             text: {
0554                 if (selfTimer.running) {
0555                     "%1 s".arg(countdownTimer.remainingSeconds)
0556                 }
0557                 else {
0558                     "%1 s".arg(camera.selfTimerDuration)
0559                 }
0560             }
0561             font.pixelSize: Kirigami.Units.gridUnit
0562             color: {
0563                 if (selfTimer.running) {
0564                     "red"
0565                 }
0566                 else {
0567                     "white"
0568                 }
0569             }
0570         }
0571 
0572         layer.enabled: selfTimerInfo.enabled
0573         layer.effect: DropShadow {
0574             color: Material.dropShadowColor
0575             samples: 30
0576             spread: 0.5
0577         }
0578     }
0579 
0580     Rectangle {
0581         id: selfTimerRectangle
0582         visible: selfTimer.running
0583         color: "transparent"
0584         border.color: "red"
0585         border.width: Kirigami.Units.gridUnit / 6
0586         opacity: 0
0587 
0588         anchors {
0589             fill: parent
0590             centerIn: parent
0591         }
0592     }
0593 
0594     SequentialAnimation {
0595         id: selfTimerAnimation
0596         running: selfTimer.running
0597         loops: Animation.Infinite
0598 
0599         ParallelAnimation {
0600             OpacityAnimator {
0601                 target: selfTimerIcon
0602                 from: 0
0603                 to: 1
0604                 duration: 500
0605             }
0606             OpacityAnimator {
0607                 target: selfTimerRectangle
0608                 from: 0
0609                 to: 1
0610                 duration: 500
0611             }
0612         }
0613 
0614         ParallelAnimation{
0615             OpacityAnimator {
0616                 target: selfTimerIcon
0617                 from: 1
0618                 to: 0
0619                 duration: 500
0620             }
0621             OpacityAnimator {
0622                 target: selfTimerRectangle
0623                 from: 1
0624                 to: 0
0625                 duration: 500
0626             }
0627         }
0628     }
0629 }