1/* 2 SPDX-FileCopyrightText: 2020 (c) Devin Lin <espidev@gmail.com> 3 4 SPDX-License-Identifier: LGPL-3.0-or-later 5 */ 6 7import QtQuick 2.7 8import QtQuick.Layouts 1.2 9import QtQuick.Controls 2.3 10import QtQuick.Window 2.2 11import QtGraphicalEffects 1.0 12import org.kde.kirigami 2.5 as Kirigami 13import org.kde.elisa 1.0 14import ".." 15import "../shared" 16 17BasePlayerControl { 18 id: trackPlayer 19 20 property alias volume: volumeButton.sliderValue 21 22 property string image 23 property string title 24 property string artist 25 property string albumArtist 26 property string album 27 28 property bool portrait 29 30 property int imageSourceSize: 512 31 32 // hardcode slider colours because theming doesn't really make sense on top of the blurred background 33 property color sliderElapsedColor: "white" 34 property color sliderRemainingColor: "grey" 35 property color sliderHandleColor: "white" 36 37 ColumnLayout { 38 anchors.fill: parent 39 spacing: Kirigami.Units.largeSpacing 40 41 // hide arrow button 42 FlatButtonWithToolTip { 43 id: minimizePlayer 44 Layout.alignment: Qt.AlignHCenter | Qt.AlignTop 45 Layout.maximumHeight: parent.height 46 Layout.preferredHeight: Kirigami.Units.gridUnit * 2 47 Layout.maximumWidth: parent.height 48 Layout.preferredWidth: Kirigami.Units.gridUnit * 2 49 text: i18nc("minimize player", "Minimize Player") 50 icon.name: "arrow-down" 51 icon.color: "white" 52 Kirigami.Theme.colorSet: Kirigami.Theme.Complementary 53 Kirigami.Theme.inherit: false 54 onClicked: toClose.restart() 55 } 56 57 // album art 58 ImageWithFallback { 59 property double specWidth: { 60 let allowedWidth = mainWindow.width - Kirigami.Units.largeSpacing * 4; 61 let allowedHeight = mainWindow.height - Kirigami.Units.largeSpacing * 8 - (minimizePlayer.height + bottomPlayerControls.height); 62 if (allowedWidth > allowedHeight) { 63 return allowedHeight; 64 } else { 65 return allowedWidth; 66 } 67 } 68 Layout.alignment: Qt.AlignHCenter | Qt.AlignTop 69 Layout.preferredWidth: specWidth 70 Layout.minimumWidth: specWidth 71 Layout.maximumWidth: specWidth 72 Layout.preferredHeight: specWidth 73 74 asynchronous: true 75 mipmap: true 76 77 source: trackPlayer.image 78 fallback: Qt.resolvedUrl(elisaTheme.defaultAlbumImage) 79 80 sourceSize { 81 width: imageSourceSize 82 height: imageSourceSize 83 } 84 85 fillMode: Image.PreserveAspectFit 86 } 87 88 Item { Layout.fillHeight: true } 89 90 // bottom player controls 91 ColumnLayout { 92 id: bottomPlayerControls 93 spacing: Kirigami.Units.smallSpacing 94 95 Layout.alignment: Qt.AlignBottom 96 Layout.fillWidth: true 97 98 property int maxWidth: mainWindow.width - Kirigami.Units.largeSpacing * 4 99 Layout.maximumWidth: maxWidth 100 101 LabelWithToolTip { 102 id: mainLabel 103 text: title 104 wrapMode: Text.Wrap 105 Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter 106 Layout.fillWidth: true 107 elide: Text.ElideRight 108 maximumLineCount: 1 109 // Hardcoded because the footerbar blur always makes a dark-ish 110 // background, so we don't want to use a color scheme color that 111 // might also be dark 112 color: "white" 113 level: 4 114 font.weight: Font.Bold 115 font.bold: true 116 117 MouseArea { 118 id: titleMouseArea 119 hoverEnabled: true 120 anchors.left: parent.left 121 anchors.top: parent.top 122 anchors.bottom: parent.bottom 123 width: parent.implicitWidth 124 cursorShape: Qt.PointingHandCursor 125 onClicked: { 126 openNowPlaying() 127 } 128 } 129 } 130 131 LabelWithToolTip { 132 id: authorLabel 133 text: artist 134 visible: text.length > 0 135 wrapMode: Text.Wrap 136 Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter 137 Layout.fillWidth: true 138 elide: Text.ElideRight 139 maximumLineCount: 1 140 // Hardcoded because the footerbar blur always makes a dark-ish 141 // background, so we don't want to use a color scheme color that 142 // might also be dark 143 color: "white" 144 level: 4 145 146 MouseArea { 147 id: authorMouseArea 148 hoverEnabled: true 149 anchors.left: parent.left 150 anchors.top: parent.top 151 anchors.bottom: parent.bottom 152 width: parent.implicitWidth 153 cursorShape: Qt.PointingHandCursor 154 onClicked: { 155 openArtist() 156 } 157 } 158 } 159 160 LabelWithToolTip { 161 id: albumLabel 162 text: album 163 visible: text.length > 0 164 wrapMode: Text.Wrap 165 Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter 166 Layout.fillWidth: true 167 elide: Text.ElideRight 168 maximumLineCount: 1 169 // Hardcoded because the footerbar blur always makes a dark-ish 170 // background, so we don't want to use a color scheme color that 171 // might also be dark 172 color: "white" 173 level: 4 174 175 MouseArea { 176 id: albumMouseArea 177 hoverEnabled: true 178 anchors.left: parent.left 179 anchors.top: parent.top 180 anchors.bottom: parent.bottom 181 width: parent.implicitWidth 182 cursorShape: Qt.PointingHandCursor 183 onClicked: { 184 openAlbum() 185 } 186 } 187 } 188 189 // misc. player controls 190 RowLayout { 191 Layout.preferredHeight: Kirigami.Units.gridUnit * 3 192 193 // ensure white icons 194 Kirigami.Theme.colorSet: Kirigami.Theme.Complementary 195 Kirigami.Theme.inherit: false 196 197 FlatButtonWithToolTip { 198 id: infoButton 199 Layout.maximumHeight: parent.height 200 Layout.preferredHeight: Math.floor(Kirigami.Units.gridUnit * 2.5) 201 Layout.maximumWidth: height 202 Layout.preferredWidth: height 203 text: i18nc("show track information", "Show Info") 204 icon.name: "documentinfo" 205 icon.color: "white" 206 onClicked: openNowPlaying() 207 } 208 209 FlatButtonWithToolTip { 210 id: shuffleButton 211 Layout.maximumHeight: parent.height 212 Layout.preferredHeight: Math.floor(Kirigami.Units.gridUnit * 2.5) 213 Layout.maximumWidth: height 214 Layout.preferredWidth: height 215 text: i18nc("toggle shuffle mode for playlist", "Toggle Shuffle") 216 icon.name: "media-playlist-shuffle" 217 icon.color: "white" 218 onClicked: trackPlayer.shuffle = !trackPlayer.shuffle 219 checked: trackPlayer.shuffle 220 } 221 222 FlatButtonWithToolTip { 223 id: repeatButton 224 Layout.maximumHeight: parent.height 225 Layout.preferredHeight: Math.floor(Kirigami.Units.gridUnit * 2.5) 226 Layout.maximumWidth: height 227 Layout.preferredWidth: height 228 text: { 229 const map = { 230 0: i18n("Current: Don't repeat tracks"), 231 1: i18n("Current: Repeat current track"), 232 2: i18n("Current: Repeat all tracks in playlist") 233 } 234 return map[trackPlayer.repeat] 235 } 236 icon.name: { 237 const map = { 238 0: "media-repeat-none", 239 1: "media-repeat-single", 240 2: "media-repeat-all" 241 } 242 return map[trackPlayer.repeat] 243 } 244 icon.color: "white" 245 checked: repeat !== 0 246 onClicked: { 247 let nextRepeat = trackPlayer.repeat + 1 248 if (nextRepeat >= 3) { 249 nextRepeat = 0 250 } 251 trackPlayer.repeat = nextRepeat 252 } 253 onPressAndHold: { 254 playlistModeMenu.popup() 255 } 256 257 Menu { 258 id: playlistModeMenu 259 260 PlaylistModeItem { 261 text: i18n("Playlist") 262 mode: MediaPlayListProxyModel.Playlist 263 } 264 PlaylistModeItem { 265 text: i18n("One") 266 mode: MediaPlayListProxyModel.One 267 } 268 PlaylistModeItem { 269 text: i18n("None") 270 mode: MediaPlayListProxyModel.None 271 } 272 } 273 } 274 } 275 276 // duration slider 277 DurationSlider { 278 Layout.fillWidth: true 279 Layout.maximumHeight: Math.floor(Kirigami.Units.gridUnit * 2.5) 280 position: trackPlayer.position 281 duration: trackPlayer.duration 282 seekable: trackPlayer.seekable 283 playEnabled: trackPlayer.playEnabled 284 onSeek: trackPlayer.seek(position) 285 286 // this color works well over the blurred/darkened background 287 labelColor: "white" 288 } 289 290 // bottom play controls 291 RowLayout { 292 Layout.alignment: Qt.AlignHCenter 293 Layout.preferredHeight: Math.floor(Kirigami.Units.gridUnit * 2.5) 294 295 // ensure white icons 296 Kirigami.Theme.colorSet: Kirigami.Theme.Complementary 297 Kirigami.Theme.inherit: false 298 299 // volume button 300 MobileVolumeButton { 301 id: volumeButton 302 muted: trackPlayer.muted 303 Layout.maximumHeight: parent.height 304 Layout.preferredHeight: Math.floor(Kirigami.Units.gridUnit * 2.5) 305 Layout.maximumWidth: height 306 Layout.preferredWidth: height 307 } 308 309 Item { Layout.fillWidth: true } 310 311 FlatButtonWithToolTip { 312 id: skipBackwardButton 313 Layout.maximumHeight: parent.height 314 Layout.preferredHeight: Math.floor(Kirigami.Units.gridUnit * 2.5) 315 Layout.maximumWidth: height 316 Layout.preferredWidth: height 317 enabled: skipBackwardEnabled 318 text: i18nc("skip backward in playlists", "Skip Backward") 319 onClicked: trackPlayer.playPrevious() 320 icon.name: trackPlayer.LayoutMirroring.enabled ? "media-skip-forward" : "media-skip-backward" 321 icon.width: Kirigami.Units.gridUnit 322 icon.height: Kirigami.Units.gridUnit 323 icon.color: "white" 324 } 325 326 FlatButtonWithToolTip { 327 id: playPauseButton 328 Layout.maximumHeight: parent.height 329 Layout.preferredHeight: Math.floor(Kirigami.Units.gridUnit * 2.5) 330 Layout.maximumWidth: height 331 Layout.preferredWidth: height 332 enabled: playEnabled 333 text: i18nc("toggle play and pause for the audio player", "Toggle Play and Pause") 334 onClicked: trackPlayer.isPlaying ? trackPlayer.pause() : trackPlayer.play() 335 icon.name: trackPlayer.isPlaying? "media-playback-pause" : "media-playback-start" 336 icon.width: Kirigami.Units.gridUnit 337 icon.height: Kirigami.Units.gridUnit 338 icon.color: "white" 339 } 340 341 FlatButtonWithToolTip { 342 id: skipForwardButton 343 Layout.maximumHeight: parent.height 344 Layout.preferredHeight: Math.floor(Kirigami.Units.gridUnit * 2.5) 345 Layout.maximumWidth: height 346 Layout.preferredWidth: height 347 enabled: skipForwardEnabled 348 text: i18nc("skip forward in playlists", "Skip Forward") 349 onClicked: trackPlayer.playNext() 350 icon.name: trackPlayer.LayoutMirroring.enabled ? "media-skip-backward" : "media-skip-forward" 351 icon.width: Kirigami.Units.gridUnit 352 icon.height: Kirigami.Units.gridUnit 353 icon.color: "white" 354 } 355 356 Item { Layout.fillWidth: true } 357 358 FlatButtonWithToolTip { 359 id: showPlaylistButton 360 Layout.maximumHeight: parent.height 361 Layout.preferredHeight: Math.floor(Kirigami.Units.gridUnit * 2.5) 362 Layout.maximumWidth: height 363 Layout.preferredWidth: height 364 text: i18nc("show the playlist", "Show Playlist") 365 onClicked: playlistDrawer.open() 366 icon.name: "view-media-playlist" 367 icon.width: Kirigami.Units.gridUnit 368 icon.height: Kirigami.Units.gridUnit 369 icon.color: "white" 370 } 371 } 372 } 373 } 374} 375