Warning, /multimedia/kasts/src/qml/Desktop/DesktopPlayerControls.qml is written in an unsupported language. File is not indexed.
0001 /** 0002 * SPDX-FileCopyrightText: 2023 Bart De Vries <bart@mogwai.be> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 0007 import QtQuick 0008 import QtQuick.Controls as Controls 0009 import QtQuick.Layouts 0010 import QtQml.Models 0011 0012 import org.kde.kirigami as Kirigami 0013 import org.kde.kmediasession 0014 0015 import org.kde.kasts 0016 import org.kde.kasts.settings 0017 0018 import ".." 0019 0020 FocusScope { 0021 id: desktopPlayerControls 0022 implicitHeight: playerControlToolBar.implicitHeight + Kirigami.Units.largeSpacing * 2 0023 0024 property alias chapterModel: chapterModel 0025 /* 0026 * Emmited when User uses the Item as a handle to resize the layout. 0027 * y: difference to previous position 0028 * offset: cursor offset (y coordinate relative to this Item, where dragging 0029 * begun) 0030 */ 0031 signal handlePositionChanged(int y, int offset) 0032 0033 Rectangle { 0034 id: toolbarBackground 0035 anchors.fill: parent 0036 0037 opacity: 0.7 0038 0039 //set background color 0040 Kirigami.Theme.inherit: false 0041 Kirigami.Theme.colorSet: Kirigami.Theme.Header 0042 color: Kirigami.Theme.backgroundColor 0043 0044 MouseArea { 0045 anchors.fill: parent 0046 property int dragStartOffset: 0 0047 0048 cursorShape: Qt.SizeVerCursor 0049 0050 onPressed: (mouse) => { 0051 dragStartOffset = mouse.y 0052 } 0053 0054 onPositionChanged: (mouse) => { 0055 desktopPlayerControls.handlePositionChanged(mouse.y, dragStartOffset) 0056 } 0057 0058 drag.axis: Drag.YAxis 0059 drag.threshold: 1 0060 } 0061 } 0062 0063 RowLayout { 0064 id: playerControlToolBar 0065 0066 anchors.fill: parent 0067 anchors.topMargin: Kirigami.Units.largeSpacing 0068 anchors.bottomMargin: Kirigami.Units.largeSpacing 0069 anchors.rightMargin: Kirigami.Units.smallSpacing 0070 anchors.leftMargin: Kirigami.Units.smallSpacing 0071 0072 property int audioSliderNiceMinimumWidth: 300 0073 property int audioSliderAbsoluteMinimumWidth: 200 0074 0075 // size of volumeButton serves as size of extra buttons too 0076 // this is chosen because the volumeButton is always visible 0077 property bool tooNarrowExtra: playerControlToolBar.width - (audioButtons.width + 4 * volumeButton.width + (chapterAction.visible ? chapterTextMetric.width : 0) + extraButtonsTextMetric.width) < audioSliderNiceMinimumWidth + 40 0078 0079 property bool tooNarrowChapter: playerControlToolBar.width - (audioButtons.width + 4 * volumeButton.width + (chapterAction.visible ? chapterTextMetric.width : 0)) < audioSliderNiceMinimumWidth + 20 0080 0081 property bool tooNarrowOverflow: playerControlToolBar.width - (audioButtons.width + 4 * volumeButton.width) < audioSliderNiceMinimumWidth 0082 0083 property bool tooNarrowAudioLabels: playerControlToolBar.width - (audioButtons.width + 2 * volumeButton.width) < audioSliderAbsoluteMinimumWidth 0084 0085 clip: true 0086 0087 Loader { 0088 Layout.fillHeight: true 0089 Layout.preferredWidth: height 0090 active: headerBar.handlePosition === 0 0091 visible: active 0092 sourceComponent: imageComponent 0093 } 0094 0095 Component { 0096 id: imageComponent 0097 ImageWithFallback { 0098 id: frontImage 0099 imageSource: headerMetaData.image 0100 absoluteRadius: Kirigami.Units.smallSpacing 0101 visible: headerBar.handlePosition === 0 0102 MouseArea { 0103 anchors.fill: parent 0104 cursorShape: Qt.PointingHandCursor 0105 onClicked: { 0106 headerBar.openFullScreenImage(); 0107 } 0108 } 0109 } 0110 } 0111 0112 RowLayout { 0113 id: audioButtons 0114 Controls.ToolButton { 0115 icon.name: "media-seek-backward" 0116 onClicked: AudioManager.skipBackward() 0117 enabled: AudioManager.canSkipBackward 0118 0119 Controls.ToolTip.visible: hovered 0120 Controls.ToolTip.delay: Kirigami.Units.toolTipDelay 0121 Controls.ToolTip.text: i18n("Seek backward") 0122 } 0123 Controls.ToolButton { 0124 id: playButton 0125 icon.name: AudioManager.playbackState === KMediaSession.PlayingState ? "media-playback-pause" : "media-playback-start" 0126 onClicked: AudioManager.playbackState === KMediaSession.PlayingState ? AudioManager.pause() : AudioManager.play() 0127 enabled: AudioManager.canPlay 0128 0129 Controls.ToolTip.visible: hovered 0130 Controls.ToolTip.delay: Kirigami.Units.toolTipDelay 0131 Controls.ToolTip.text: AudioManager.playbackState === KMediaSession.PlayingState ? i18n("Pause") : i18n("Play") 0132 } 0133 Controls.ToolButton { 0134 icon.name: "media-seek-forward" 0135 onClicked: AudioManager.skipForward() 0136 enabled: AudioManager.canSkipForward 0137 0138 Controls.ToolTip.visible: hovered 0139 Controls.ToolTip.delay: Kirigami.Units.toolTipDelay 0140 Controls.ToolTip.text: i18n("Seek forward") 0141 } 0142 Controls.ToolButton { 0143 icon.name: "media-skip-forward" 0144 onClicked: AudioManager.next() 0145 enabled: AudioManager.canGoNext 0146 0147 Controls.ToolTip.visible: hovered 0148 Controls.ToolTip.delay: Kirigami.Units.toolTipDelay 0149 Controls.ToolTip.text: i18n("Skip forward") 0150 } 0151 Controls.ToolButton { 0152 id: playbackRateButton 0153 text: AudioManager.playbackRate.toFixed(2) + "x" 0154 checkable: true 0155 checked: playbackRateMenu.visible 0156 onClicked: { 0157 if (playbackRateMenu.visible) { 0158 playbackRateMenu.dismiss(); 0159 } else { 0160 playbackRateMenu.popup(playbackRateButton, 0, playbackRateButton.height); 0161 } 0162 } 0163 padding: 0 0164 implicitWidth: playButton.width * 1.5 0165 implicitHeight: playButton.height 0166 0167 Controls.ToolTip.visible: hovered 0168 Controls.ToolTip.delay: Kirigami.Units.toolTipDelay 0169 Controls.ToolTip.text: i18n("Playback rate:") + " " + AudioManager.playbackRate.toFixed(2) + "x" 0170 } 0171 } 0172 0173 Controls.Label { 0174 id: currentPositionLabel 0175 text: AudioManager.formattedPosition 0176 visible: !playerControlToolBar.tooNarrowAudioLabels 0177 Layout.alignment: Qt.AlignVCenter 0178 } 0179 0180 ChapterSlider { 0181 id: durationSlider 0182 model: chapterModel 0183 enabled: AudioManager.entry && AudioManager.PlaybackState != AudioManager.StoppedState && AudioManager.canPlay 0184 Layout.fillWidth: true 0185 Layout.alignment: Qt.AlignVCenter 0186 } 0187 0188 Item { 0189 id: durationLabel 0190 visible: !playerControlToolBar.tooNarrowAudioLabels 0191 Layout.preferredHeight: endLabel.implicitHeight 0192 Layout.preferredWidth: endLabel.implicitWidth 0193 Layout.rightMargin: Kirigami.Units.largeSpacing 0194 Layout.alignment: Qt.AlignVCenter 0195 0196 Controls.Label { 0197 id: endLabel 0198 anchors.right: parent.right 0199 anchors.verticalCenter: parent.verticalCenter 0200 text: (SettingsManager.toggleRemainingTime) ? 0201 "-" + AudioManager.formattedLeftDuration 0202 : AudioManager.formattedDuration 0203 } 0204 0205 MouseArea { 0206 anchors.fill: parent 0207 hoverEnabled: true 0208 onClicked: { 0209 SettingsManager.toggleRemainingTime = !SettingsManager.toggleRemainingTime; 0210 SettingsManager.save(); 0211 } 0212 } 0213 } 0214 0215 RowLayout { 0216 id: extraButtons 0217 visible: !playerControlToolBar.tooNarrowOverflow 0218 Controls.ToolButton { 0219 id: chapterButton 0220 action: chapterAction 0221 display: playerControlToolBar.tooNarrowChapter ? Controls.AbstractButton.IconOnly : Controls.AbstractButton.TextBesideIcon 0222 visible: chapterAction.visible && !playerControlToolBar.tooNarrowOverflow 0223 0224 Controls.ToolTip.visible: hovered 0225 Controls.ToolTip.delay: Kirigami.Units.toolTipDelay 0226 Controls.ToolTip.text: i18nc("@action:button", "Show chapter list") 0227 } 0228 0229 Controls.ToolButton { 0230 id: infoButton 0231 action: infoAction 0232 display: playerControlToolBar.tooNarrowExtra ? Controls.AbstractButton.IconOnly : Controls.AbstractButton.TextBesideIcon 0233 visible: infoAction.visible && !playerControlToolBar.tooNarrowOverflow 0234 0235 Controls.ToolTip.visible: hovered 0236 Controls.ToolTip.delay: Kirigami.Units.toolTipDelay 0237 Controls.ToolTip.text: i18nc("@action:button", "Show episode info") 0238 } 0239 0240 Controls.ToolButton { 0241 id: sleepButton 0242 action: sleepAction 0243 display: playerControlToolBar.tooNarrowExtra ? Controls.AbstractButton.IconOnly : Controls.AbstractButton.TextBesideIcon 0244 visible: !playerControlToolBar.tooNarrowOverflow 0245 0246 Controls.ToolTip.visible: hovered 0247 Controls.ToolTip.delay: Kirigami.Units.toolTipDelay 0248 Controls.ToolTip.text: i18nc("@action:button", "Open sleep timer settings") 0249 } 0250 } 0251 0252 RowLayout { 0253 id: volumeControls 0254 Controls.ToolButton { 0255 id: volumeButton 0256 icon.name: AudioManager.muted ? "player-volume-muted" : "player-volume" 0257 enabled: AudioManager.PlaybackState != AudioManager.StoppedState && AudioManager.canPlay 0258 checked: volumePopup.visible 0259 0260 Controls.ToolTip.visible: hovered 0261 Controls.ToolTip.delay: Kirigami.Units.toolTipDelay 0262 Controls.ToolTip.text: i18nc("@action:button", "Open volume settings") 0263 0264 onClicked: { 0265 if (volumePopup.visible) { 0266 volumePopup.close(); 0267 } else { 0268 volumePopup.open(); 0269 } 0270 } 0271 0272 Controls.Popup { 0273 id: volumePopup 0274 x: -padding 0275 y: volumeButton.height 0276 0277 focus: true 0278 padding: Kirigami.Units.smallSpacing 0279 contentWidth: volumeButtonVertical.implicitWidth 0280 0281 contentItem: ColumnLayout { 0282 VolumeSlider { 0283 id: volumeSlider 0284 } 0285 0286 Controls.ToolButton { 0287 id: volumeButtonVertical 0288 enabled: AudioManager.PlaybackState != AudioManager.StoppedState && AudioManager.canPlay 0289 icon.name: AudioManager.muted ? "player-volume-muted" : "player-volume" 0290 onClicked: AudioManager.muted = !AudioManager.muted 0291 0292 Controls.ToolTip.visible: hovered 0293 Controls.ToolTip.delay: Kirigami.Units.toolTipDelay 0294 Controls.ToolTip.text: i18nc("@action:button", "Toggle mute") 0295 0296 } 0297 } 0298 } 0299 } 0300 } 0301 0302 Controls.ToolButton { 0303 id: overflowButton 0304 icon.name: "overflow-menu" 0305 display: Controls.AbstractButton.IconOnly 0306 visible: playerControlToolBar.tooNarrowOverflow 0307 checked: overflowMenu.visible 0308 0309 Controls.ToolTip.visible: hovered 0310 Controls.ToolTip.delay: Kirigami.Units.toolTipDelay 0311 Controls.ToolTip.text: i18nc("@action:button", "Show more") 0312 0313 onClicked: { 0314 if (overflowMenu.visible) { 0315 overflowMenu.dismiss(); 0316 } else { 0317 overflowMenu.popup(0, overflowButton.height) 0318 } 0319 } 0320 0321 Controls.Menu { 0322 id: overflowMenu 0323 contentData: extraActions 0324 onVisibleChanged: { 0325 if (visible) { 0326 for (var i in contentData) { 0327 overflowMenu.contentData[i].visible = overflowMenu.contentData[i].action.visible; 0328 overflowMenu.contentData[i].height = 0329 (overflowMenu.contentData[i].action.visible) ? overflowMenu.contentData[i].implicitHeight : 0 // workaround for qqc2-breeze-style 0330 } 0331 } 0332 } 0333 } 0334 } 0335 } 0336 0337 // Actions which will be used to create buttons on toolbar or in overflow menu 0338 Kirigami.Action { 0339 id: chapterAction 0340 property bool visible: AudioManager.entry && chapterList.count !== 0 0341 text: i18nc("@action:button", "Chapters") 0342 icon.name: "view-media-playlist" 0343 onTriggered: chapterOverlay.open(); 0344 } 0345 0346 Kirigami.Action { 0347 id: infoAction 0348 property bool visible: AudioManager.entry 0349 text: i18nc("@action:button", "Show Info") 0350 icon.name: "documentinfo" 0351 onTriggered: entryDetailsOverlay.open(); 0352 } 0353 0354 Kirigami.Action { 0355 id: sleepAction 0356 checkable: true 0357 checked: AudioManager.remainingSleepTime > 0 0358 property bool visible: true 0359 text: i18nc("@action:button", "Sleep Timer") 0360 icon.name: "clock" 0361 onTriggered: { 0362 toggle(); // only set the on/off state based on sleep timer state 0363 sleepTimerDialog.open(); 0364 } 0365 } 0366 0367 property var extraActions: [ chapterAction, 0368 infoAction, 0369 sleepAction ] 0370 0371 TextMetrics { 0372 id: chapterTextMetric 0373 text: chapterAction.text 0374 } 0375 0376 TextMetrics { 0377 id: extraButtonsTextMetric 0378 text: infoAction.text + sleepAction.text 0379 } 0380 0381 ChapterModel { 0382 id: chapterModel 0383 entry: AudioManager.entry ? AudioManager.entry : null 0384 duration: AudioManager.duration 0385 } 0386 0387 Kirigami.Dialog { 0388 id: chapterOverlay 0389 0390 showCloseButton: false 0391 0392 title: i18n("Chapters") 0393 0394 ListView { 0395 id: chapterList 0396 0397 currentIndex: -1 0398 0399 implicitWidth: Kirigami.Units.gridUnit * 30 0400 implicitHeight: Kirigami.Units.gridUnit * 25 0401 0402 model: chapterModel 0403 delegate: ChapterListDelegate { 0404 id: chapterDelegate 0405 width: chapterList.width 0406 entry: AudioManager.entry ? AudioManager.entry : null 0407 overlay: chapterOverlay 0408 } 0409 } 0410 } 0411 0412 Kirigami.Dialog { 0413 id: entryDetailsOverlay 0414 preferredWidth: Kirigami.Units.gridUnit * 30 0415 0416 showCloseButton: false 0417 0418 title: AudioManager.entry ? AudioManager.entry.title : i18n("No Track Title") 0419 padding: Kirigami.Units.largeSpacing 0420 0421 Controls.Label { 0422 id: text 0423 text: AudioManager.entry ? AudioManager.entry.adjustedContent(width, font.pixelSize) : i18n("No track loaded") 0424 verticalAlignment: Text.AlignTop 0425 baseUrl: AudioManager.entry ? AudioManager.entry.baseUrl : "" 0426 textFormat: Text.RichText 0427 wrapMode: Text.WordWrap 0428 onLinkHovered: { 0429 cursorShape: Qt.PointingHandCursor; 0430 } 0431 onLinkActivated: (link) => { 0432 if (link.split("://")[0] === "timestamp") { 0433 if (AudioManager.entry && AudioManager.entry.enclosure) { 0434 AudioManager.seek(link.split("://")[1]); 0435 } 0436 } else { 0437 Qt.openUrlExternally(link); 0438 } 0439 } 0440 } 0441 } 0442 0443 PlaybackRateMenu { 0444 id: playbackRateMenu 0445 parentButton: playbackRateButton 0446 } 0447 }