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 }