diff --git a/main.qml b/main.qml index c0a9822..822832a 100644 --- a/main.qml +++ b/main.qml @@ -1,857 +1,855 @@ import QtQuick 2.10 import QtQuick.Controls 2.10 import QtQuick.Layouts 1.3 import QtGraphicalEffects 1.0 import QtQuick.Controls.Material 2.1 import "utils" import "widgets" import "widgets/PlaylistsView" import "widgets/MainPlaylist" import "widgets/SettingsView" import "widgets/SearchView" //import "widgets/CloudView" import "view_models" import "view_models/BabeTable" import "services/local" import "services/web" //import "services/web/Spotify" import "view_models/BabeGrid" import "widgets/InfoView" import "db/Queries.js" as Q import "utils/Help.js" as H import "utils/Player.js" as Player import org.kde.kirigami 2.7 as Kirigami import org.kde.mauikit 1.0 as Maui import Player 1.0 import AlbumsList 1.0 import TracksList 1.0 import PlaylistsList 1.0 Maui.ApplicationWindow { id: root title: qsTr("vvave") /***************************************************/ /******************** ALIASES ********************/ /*************************************************/ property alias mainPlaylist: mainPlaylist property alias selectionBar: _selectionBar property alias progressBar: progressBar property alias dialog : _dialogLoader.item Maui.App.iconName: "qrc:/assets/vvave.svg" Maui.App.description: qsTr("VVAVE will handle your whole music collection by retreaving semantic information from the web. Just relax, enjoy and discover your new music ") /***************************************************/ /******************** PLAYBACK ********************/ /*************************************************/ property bool isShuffle: Maui.FM.loadSettings("SHUFFLE","PLAYBACK", false) property var currentTrack: ({ fav: "0", stars: "0" }) property int currentTrackIndex: -1 property int prevTrackIndex: 0 property string currentArtwork: !mainlistEmpty ? mainPlaylist.listModel.get(0).artwork : "" property bool currentBabe: currentTrack.fav == "0" ? false : true property alias durationTimeLabel: player.duration property string progressTimeLabel: player.transformTime((player.duration/1000) *(player.pos/ 1000)) property alias isPlaying: player.playing property int onQueue: 0 property bool mainlistEmpty: !mainPlaylist.table.count > 0 /***************************************************/ /******************** HANDLERS ********************/ /*************************************************/ readonly property var viewsIndex: ({ tracks: 0, albums: 1, artists: 2, playlists: 3, folders: 4, cloud : 5, youtube: 6, search: 7}) property string syncPlaylist: "" property bool sync: false property bool focusView : false property bool selectionMode : false /***************************************************/ /******************** UI COLORS *******************/ /*************************************************/ readonly property color babeColor: "#f84172" /*SIGNALS*/ signal missingAlert(var track) /*HANDLE EVENTS*/ onClosing: Player.savePlaylist() onMissingAlert: { var message = qsTr("Missing file...") var messageBody = track.title + " by " + track.artist + " is missing.\nDo you want to remove it from your collection?" notify("dialog-question", message, messageBody, function () { mainPlaylist.list.remove(mainPlaylist.table.currentIndex) }) } /*COMPONENTS*/ Player { id: player volume: 100 onFinishedChanged: if (!mainlistEmpty) { if (currentTrack.url) mainPlaylist.list.countUp(currentTrackIndex) Player.nextTrack() } } FloatingDisk { id: _floatingDisk opacity: 1 - _drawer.position } headBar.middleContent : Maui.ActionGroup { id: _actionGroup // Layout.fillWidth: true Layout.fillHeight: true Layout.minimumWidth: implicitWidth currentIndex : swipeView.currentIndex onCurrentIndexChanged: swipeView.currentIndex = currentIndex hiddenActions: [ Action { text: qsTr("Folders") icon.name: "folder" }, // Action // { // text: qsTr("Cloud") // icon.name: "folder-cloud" // }, Action { text: qsTr("YouTube") icon.name: "internet-services" } ] Action { icon.name: "view-media-track" text: qsTr("Tracks") } Action { text: qsTr("Albums") icon.name: /*"album"*/ "view-media-album-cover" } Action { text: qsTr("Artists") icon.name: "view-media-artist" } Action { text: qsTr("Playlists") icon.name: "view-media-playlist" } } onSearchButtonClicked: { _actionGroup.currentIndex = viewsIndex.search // searchView.searchInput.forceActiveFocus() } Loader { id: _dialogLoader } InfoView { id: infoView maxWidth: parent.width * 0.8 maxHeight: parent.height * 0.9 } Loader { id: _focusViewLoader active: focusView source: "widgets/FocusView.qml" } Component { id: _shareDialogComponent Maui.ShareDialog {} } Component { id: _fmDialogComponent Maui.FileDialog { } } SourcesDialog { id: sourcesDialog } mainMenu: [ // Maui.MenuItem // { // text: "Vvave Stream" // icon.name: "headphones" // onTriggered: // { // pageStack.currentIndex = 1 // currentView = viewsIndex.vvave // } // }, // Maui.MenuItem // { // text: qsTr("Linking") // icon.name: "view-links" // onTriggered: // { // pageStack.currentIndex = 1 // currentView = viewsIndex.linking // if(!isLinked) linkingView.linkingConf.open() // } // }, // Maui.MenuItem // { // text: qsTr("Cloud") // icon.name: "folder-cloud" // onTriggered: // { // pageStack.currentIndex = 1 // currentView = viewsIndex.cloud // } // }, // Maui.MenuItem // { // text: qsTr("Spotify") // icon.name: "internet-services" // onTriggered: // { // pageStack.currentIndex = 1 // currentView = viewsIndex.spotify // } // }, MenuSeparator{}, MenuItem { text: qsTr("Sources") icon.name: "folder-add" onTriggered: sourcesDialog.open() }, MenuItem { text: qsTr("Open") icon.name: "folder-add" onTriggered: { _dialogLoader.sourceComponent = _fmDialogComponent root.dialog.settings.onlyDirs = false root.dialog.currentPath = "file:///home/camilo/Music" root.dialog.settings.filterType = Maui.FMList.AUDIO console.log("SETTIGN FILTER TYPE FISR", root.dialog.settings.filterType, Maui.FMList.AUDIO) root.dialog.show(function(paths) { vvave.openUrls(paths) root.dialog.close() }) } }/*, Menu { title: qsTr("Collection") // icon.name: "settings-configure" MenuItem { text: qsTr("Re-Scan") onTriggered: bae.refreshCollection(); } MenuItem { text: qsTr("Refresh...") onTriggered: H.refreshCollection(); } MenuItem { text: qsTr("Clean") onTriggered: bae.removeMissingTracks(); } }*/ // Maui.Menu // { // title: qsTr("Settings...") // // Kirigami.Action // // { // // text: "Brainz" // // Kirigami.Action // // { // // id: brainzToggle // // text: checked ? "Turn OFF" : "Turn ON" // // checked: bae.brainzState() // // checkable: true // // onToggled: // // { // // checked = !checked // // bae.saveSetting("AUTO", checked, "BRAINZ") // //// bae.brainz(checked) // // } // // } // // } // Maui.MenuItem // { // text: "Info label" + checked ? "ON" : "OFF" // checked: infoLabels // checkable: true // onToggled: // { // infoLabels = checked // bae.saveSetting("LABELS", infoLabels ? true : false, "PLAYBACK") // } // } // Maui.MenuItem // { // text: "Autoplay" // checked: autoplay // checkable: true // onToggled: // { // autoplay = checked // bae.saveSetting("AUTOPLAY", autoplay ? true : false, "BABE") // } // } // } ] Playlists { id: playlistsList } PlaylistDialog { id: playlistDialog } sideBar: Maui.AbstractSideBar { id: _drawer focus: true width: visible ? Math.min(Kirigami.Units.gridUnit * (Kirigami.Settings.isMobile? 18 : 15), root.width) : 0 modal: !isWide closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent height: _drawer.modal ? implicitHeight - _mainPage.footer.height : implicitHeight MainPlaylist { id: mainPlaylist anchors.fill: parent Connections { target: mainPlaylist onCoverPressed: Player.appendAll(tracks) onCoverDoubleClicked: Player.playAll(tracks) } } } Maui.Page { id: _mainPage anchors.fill: parent footer: ColumnLayout { id: _footerLayout visible: !mainlistEmpty || isPlaying height: visible ? Maui.Style.toolBarHeight * 1.2 : 0 width: parent.width spacing: 0 Kirigami.Separator { Layout.fillWidth: true } Slider { id: progressBar Layout.preferredHeight: Maui.Style.unit * (Kirigami.Settings.isMobile ? 6 : 8) Layout.fillWidth: true padding: 0 from: 0 to: 1000 value: player.pos spacing: 0 focus: true onMoved: { player.pos = value } background: Rectangle { implicitWidth: progressBar.width implicitHeight: progressBar.height width: progressBar.availableWidth height: implicitHeight color: "transparent" Rectangle { width: progressBar.visualPosition * parent.width height: progressBar.height color: Kirigami.Theme.highlightColor } } handle: Rectangle { x: progressBar.leftPadding + progressBar.visualPosition * (progressBar.availableWidth - width) y: -(progressBar.height * 0.8) implicitWidth: progressBar.pressed ? Maui.Style.iconSizes.medium : 0 implicitHeight: progressBar.pressed ? Maui.Style.iconSizes.medium : 0 radius: progressBar.pressed ? Maui.Style.iconSizes.medium : 0 color: Kirigami.Theme.highlightColor } } Maui.ToolBar { Layout.fillHeight: true Layout.fillWidth: true position: ToolBar.Footer middleContent: [ ToolButton { id: babeBtnIcon icon.name: "love" enabled: currentTrackIndex >= 0 icon.color: currentBabe ? babeColor : Kirigami.Theme.textColor onClicked: if (!mainlistEmpty) { mainPlaylist.list.fav(currentTrackIndex, !(mainPlaylist.listModel.get(currentTrackIndex).fav == "1")) currentBabe = mainPlaylist.listModel.get(currentTrackIndex).fav == "1" } }, ToolButton { icon.name: "media-skip-backward" icon.color: Kirigami.Theme.textColor onClicked: Player.previousTrack() onPressAndHold: Player.playAt(prevTrackIndex) }, ToolButton { id: playIcon enabled: currentTrackIndex >= 0 icon.color: Kirigami.Theme.textColor icon.name: isPlaying ? "media-playback-pause" : "media-playback-start" onClicked: player.playing = !player.playing }, ToolButton { id: nextBtn icon.color: Kirigami.Theme.textColor icon.name: "media-skip-forward" onClicked: Player.nextTrack() onPressAndHold: Player.playAt(Player.shuffle()) }, ToolButton { id: shuffleBtn icon.color: babeColor icon.name: isShuffle ? "media-playlist-shuffle" : "media-playlist-normal" onClicked: { isShuffle = !isShuffle Maui.FM.saveSettings("SHUFFLE", isShuffle, "PLAYBACK") } } ] } } ColumnLayout { anchors.fill: parent SwipeView { id: swipeView Layout.fillHeight: true Layout.fillWidth: true // interactive: Kirigami.Settings.isMobile currentIndex: _actionGroup.currentIndex onCurrentIndexChanged: _actionGroup.currentIndex = currentIndex clip: true onCurrentItemChanged: currentItem.forceActiveFocus() TracksView { id: tracksView Connections { target: vvave onRefreshTables: tracksView.list.refresh() } Connections { target: tracksView onRowClicked: Player.quickPlay(tracksView.listModel.get(index)) onQuickPlayTrack: Player.quickPlay(tracksView.listModel.get(index)) onAppendTrack: Player.addTrack(tracksView.listModel.get(index)) onPlayAll: Player.playAll( tracksView.listModel.getAll()) onAppendAll: Player.appendAll( tracksView.listModel.getAll()) onQueueTrack: Player.queueTracks([tracksView.listModel.get(index)], index) } } Loader { active: SwipeView.isCurrentItem || SwipeView.isNextItem || SwipeView.isPreviousItem || item sourceComponent: AlbumsView { id: albumsView holder.emoji: "qrc:/assets/MusicBox.png" holder.isMask: false holder.title : "No Albums!" holder.body: "Add new music sources" holder.emojiSize: Maui.Style.iconSizes.huge title: count + qsTr(" albums") list.query: Albums.ALBUMS list.sortBy: Albums.ALBUM Connections { target: vvave onRefreshTables: albumsView.list.refresh() } Connections { target: albumsView onRowClicked: Player.quickPlay(track) onAppendTrack: Player.addTrack(track) onPlayTrack: Player.quickPlay(track) onAlbumCoverClicked: albumsView.populateTable(album, artist) onAlbumCoverPressedAndHold: { var query = Q.GET.albumTracks_.arg(album) query = query.arg(artist) mainPlaylist.list.clear() mainPlaylist.list.sortBy = Tracks.NONE mainPlaylist.list.query = query Player.playAt(0) } onPlayAll: Player.playAll(albumsView.listModel.getAll()) onAppendAll: Player.appendAll(albumsView.listModel.getAll()) } } } Loader { active: SwipeView.isCurrentItem || SwipeView.isNextItem || SwipeView.isPreviousItem || item sourceComponent: AlbumsView { id: artistsView holder.emoji: "qrc:/assets/MusicBox.png" holder.isMask: false holder.title : qsTr("No Artists!") holder.body: qsTr("Add new music sources") holder.emojiSize: Maui.Style.iconSizes.huge title: count + qsTr(" artists") list.query: Albums.ARTISTS list.sortBy: Albums.ARTIST table.list.sortBy: Tracks.NONE Connections { target: vvave onRefreshTables: artistsView.list.refresh() } Connections { target: artistsView onRowClicked: Player.quickPlay(track) onAppendTrack: Player.addTrack(track) onPlayTrack: Player.quickPlay(track) onAlbumCoverClicked: artistsView.populateTable(undefined, artist) onAlbumCoverPressedAndHold: { var query = Q.GET.artistTracks_.arg(artist) mainPlaylist.list.clear() mainPlaylist.list.sortBy = Tracks.NONE mainPlaylist.list.query = query Player.playAt(0) } onPlayAll: Player.artistsView(albumsView.listModel.getAll()) onAppendAll: Player.artistsView(albumsView.listModel.getAll()) } } } Loader { active: SwipeView.isCurrentItem || SwipeView.isNextItem || SwipeView.isPreviousItem || item sourceComponent: PlaylistsView { id: playlistsView Connections { target: playlistsView onRowClicked: Player.quickPlay(track) onAppendTrack: Player.addTrack(track) onPlayTrack: Player.quickPlay(track) onAppendAll: Player.appendAll(playlistsView.listModel.getAll()) onSyncAndPlay: { Player.playAll(playlistsView.listModel.getAll()) root.sync = true root.syncPlaylist = playlist } onPlayAll: Player.playAll(playlistsView.listModel.getAll()) } } } Loader { active: SwipeView.isCurrentItem || item sourceComponent: FoldersView { id: foldersView Connections { target: vvave onRefreshTables: foldersView.populate() } Connections { target: foldersView.list onRowClicked: Player.quickPlay(foldersView.list.model.get(index)) onQuickPlayTrack: Player.quickPlay(foldersView.list.model.get(index)) onAppendTrack: Player.addTrack(foldersView.listModel.get(index)) onPlayAll: Player.playAll(foldersView.listModel.getAll()) onAppendAll: Player.appendAll(foldersView.listModel.getAll()) onQueueTrack: Player.queueTracks([foldersView.list.model.get(index)], index) } } } // Loader // { // active: SwipeView.isCurrentItem || SwipeView.isNextItem || SwipeView.isPreviousItem || item // sourceComponent: CloudView // { // id: cloudView // } // } Loader { active: SwipeView.isCurrentItem || item sourceComponent: YouTube { id: youtubeView } } Loader { active: SwipeView.isCurrentItem || (item && item.listView.count > 0) sourceComponent: SearchTable { id: searchView Connections { target: searchView onRowClicked: Player.quickPlay(searchView.listModel.get(index)) onQuickPlayTrack: Player.quickPlay(searchView.listModel.get(index)) onAppendTrack: Player.addTrack(searchView.listModel.get(index)) onPlayAll: Player.playAll(searchView.listModel.getAll()) onAppendAll: Player.appendAll(searchView.listModel.getAll()) onArtworkDoubleClicked: { var query = Q.GET.albumTracks_.arg( searchView.listModel.get( index).album) query = query.arg(searchView.listModel.get(index).artist) mainPlaylist.list.clear() mainPlaylist.list.sortBy = Tracks.NONE mainPlaylist.list.query = query Player.playAt(0) } } } } } - Maui.SelectionBar + SelectionBar { id: _selectionBar property alias listView: _selectionBar.selectionList + Layout.maximumWidth: 500 + Layout.minimumWidth: 100 Layout.fillWidth: true + Layout.alignment: Qt.AlignCenter Layout.margins: Maui.Style.space.big Layout.topMargin: Maui.Style.space.small Layout.bottomMargin: Maui.Style.space.big - onIconClicked: _contextMenu.popup() + onExitClicked: { root.selectionMode = false clear() } - - SelectionBarMenu - { - id: _contextMenu - } } } } /*CONNECTIONS*/ Connections { target: vvave onRefreshTables: { if(size>0) root.notify("emblem-info", "Collection updated", size+" new tracks added...") } // onRefreshTracks: H.refreshTracks() // onRefreshAlbums: H.refreshAlbums() // onRefreshArtists: H.refreshArtists() // onCoverReady: // { // root.currentArtwork = path // currentTrack.artwork = currentArtwork // mainPlaylist.list.update(currentTrack, currentTrackIndex); // } // onTrackLyricsReady: // { // console.log(lyrics) // if (url === currentTrack.url) // Player.setLyrics(lyrics) // } // onSkipTrack: Player.nextTrack() // onBabeIt: if (!mainlistEmpty) // { // mainPlaylist.list.fav(currentTrackIndex, !(mainPlaylist.list.get(currentTrackIndex).fav == "1")) // currentBabe = mainPlaylist.list.get(currentTrackIndex).fav == "1" // } onOpenFiles: { Player.appendTracksAt(tracks, 0) Player.playAt(0) } } Component.onCompleted: { if(isAndroid) { Maui.Android.statusbarColor(Kirigami.Theme.backgroundColor, true) Maui.Android.navBarColor(Kirigami.Theme.backgroundColor, true) } } } diff --git a/qml.qrc b/qml.qrc index 4d855d0..99cda54 100644 --- a/qml.qrc +++ b/qml.qrc @@ -1,63 +1,63 @@ main.qml widgets/TracksView.qml widgets/AlbumsView.qml assets/cover.png data_models/db_model.qml db/script.sql utils/Player.js db/Queries.js utils/Help.js assets/bars.gif view_models/BabeGrid/BabeAlbum.qml view_models/BabeGrid/BabeGrid.qml view_models/BabeTable/TableDelegate.qml view_models/BabeTable/BabeTable.qml view_models/BabeTable/TableMenu.qml view_models/ColorTagsBar.qml widgets/MainPlaylist/MainPlaylist.qml widgets/MainPlaylist/PlaylistMenu.qml widgets/PlaylistsView/PlaylistsView.qml widgets/InfoView/InfoView.qml widgets/InfoView/LyricsView.qml widgets/PlaylistsView/PlaylistsViewModel.qml utils/Props.js widgets/SettingsView/SourcesDialog.qml assets/banner.png assets/banner.svg view_models/BabeTable/PlaylistDialog.qml view_models/BabeList.qml assets/banner-yellow.png widgets/SearchView/SearchTable.qml widgets/SearchView/SearchSuggestions.qml services/web/YouTube.qml services/web/YoutubeViewer.qml services/web/YoutubePlayer.qml services/web/YoutubeHelper.js services/web/YoutubePlayer_A.qml services/local/LinkingDialog.qml services/local/LinkingView.qml services/local/LinkingListModel.qml assets/vvave.notifyrc assets/vvave.png widgets/MainPlaylist/AlbumsRoll.qml assets/heart_indicator.gif assets/heart_indicator_white.gif services/web/Spotify/Spotify.qml services/web/Spotify/LoginForm.qml services/web/Spotify/spotify.html widgets/FloatingDisk.qml services/web/WebView.qml services/web/WebView_A.qml widgets/FoldersView.qml assets/vvave.svg assets/materialdesignicons-webfont.ttf widgets/CloudView/CloudView.qml - widgets/SelectionBarMenu.qml + widgets/SelectionBar.qml widgets/FocusView.qml db/script.sql diff --git a/utils/Help.js b/utils/Help.js index 71d05d8..1b0d244 100644 --- a/utils/Help.js +++ b/utils/Help.js @@ -1,85 +1,86 @@ +.import org.kde.kirigami 2.7 as Kirigami + function rootWidth() { return root.width; } function rootHeight() { return root.height; } function setStars(stars) { switch (stars) { case "0": case 0: return ""; case "1": case 1: return "\uf4CE"; case "2": case 2: return "\uf4CE \uf4CE"; case "3": case 3: return "\uf4CE \uf4CE \uf4CE"; case "4": case 4: return "\uf4CE \uf4CE \uf4CE \uf4CE"; case "5": case 5: return "\uf4CE \uf4CE \uf4CE \uf4CE \uf4CE"; default: return "error"; } } - function notify(title, body) { - if(isMobile) + if(Kirigami.Settings.isMobile) root.notify(title+"\n"+body) else bae.notify(title, body) } function addPlaylist(playlist) { playlistsView.playlistViewModel.model.insert(0, playlist) } function searchFor(query) { if(currentView !== viewsIndex.search) currentView = viewsIndex.search searchView.runSearch(query) } function addSource() { sourcesDialog.open() } function addToSelection(item) { if(selectionBar.contains(item.url)) { - selectionBar.removeAtPath(item.url) + selectionBar.removeAtUri(item.url) return } item.thumbnail= item.artwork item.icon = "audio-x-generic" item.label= item.title item.mime= "image/png" item.tooltip= item.url item.path= item.url - selectionBar.append(item) + selectionBar.append(item.url, item) } diff --git a/view_models/BabeTable/BabeTable.qml b/view_models/BabeTable/BabeTable.qml index 5425293..519d73d 100644 --- a/view_models/BabeTable/BabeTable.qml +++ b/view_models/BabeTable/BabeTable.qml @@ -1,496 +1,508 @@ import QtQuick 2.12 import QtQuick.Controls 2.12 import QtQuick.Layouts 1.3 import org.kde.kirigami 2.7 as Kirigami import org.kde.mauikit 1.0 as Maui import TracksList 1.0 import "../../utils/Player.js" as Player import "../../utils/Help.js" as H import "../../db/Queries.js" as Q import ".." BabeList { id: control // cacheBuffer : 300 property alias list : _tracksList property alias listModel : _tracksModel property alias removeDialog : _removeDialog property bool trackNumberVisible property bool coverArtVisible : false property bool allowMenu: true property bool showQuickActions : true property bool group : false property alias contextMenu : contextMenu property alias contextMenuItems : contextMenu.contentData property alias playAllBtn : playAllBtn property alias appendBtn : appendBtn signal rowClicked(int index) signal rowDoubleClicked(int index) signal rowPressed(int index) signal quickPlayTrack(int index) signal queueTrack(int index) signal appendTrack(int index) signal artworkDoubleClicked(int index) signal playAll() signal appendAll() focus: true holder.visible: list.count === 0 listView.spacing: Maui.Style.space.small * (Kirigami.Settings.isMobile ? 1.4 : 1.2) headBar.leftContent: [ ToolButton { id : playAllBtn icon.name : "media-playlist-play" onClicked: playAll() }, ToolButton { id: appendBtn icon.name : "media-playlist-append" onClicked: appendAll() }] headBar.rightContent: [ ToolButton { icon.name: "item-select" onClicked: selectionMode = !selectionMode checkable: false checked: selectionMode }, Maui.ToolButtonMenu { id: sortBtn icon.name: "view-sort" visible: list.count > 2 MenuItem { text: qsTr("Title") checkable: true checked: list.sortBy === Tracks.TITLE onTriggered: list.sortBy = Tracks.TITLE autoExclusive: true } MenuItem { text: qsTr("Track") checkable: true checked: list.sortBy === Tracks.TRACK onTriggered: list.sortBy = Tracks.TRACK autoExclusive: true } MenuItem { text: qsTr("Artist") checkable: true checked: list.sortBy === Tracks.ARTIST onTriggered: list.sortBy = Tracks.ARTIST autoExclusive: true } MenuItem { text: qsTr("Album") checkable: true checked: list.sortBy === Tracks.ALBUM onTriggered: list.sortBy = Tracks.ALBUM autoExclusive: true } MenuItem { text: qsTr("Most played") checkable: true checked: list.sortBy === Tracks.COUNT onTriggered: list.sortBy = Tracks.COUNT autoExclusive: true } MenuItem { text: qsTr("Rate") checkable: true checked: list.sortBy === Tracks.RATE onTriggered: list.sortBy = Tracks.RATE autoExclusive: true } MenuItem { text: qsTr("Favorite") checkable: true checked: list.sortBy === Tracks.FAV onTriggered: list.sortBy = Tracks.FAV autoExclusive: true } MenuItem { text: qsTr("Release date") checkable: true checked: list.sortBy === Tracks.RELEASEDATE onTriggered: list.sortBy = Tracks.RELEASEDATE autoExclusive: true } MenuItem { text: qsTr("Add date") checkable: true checked: list.sortBy === Tracks.ADDDATE onTriggered: list.sortBy = Tracks.ADDDATE autoExclusive: true } MenuSeparator{} MenuItem { text: qsTr("Group") checkable: true checked: group onTriggered: { group = !group groupBy() } } }, ToolButton { id: _filterButton icon.name: "view-filter" checkable: true visible: list.count > 10 } ] listView.header: Maui.ToolBar { Kirigami.Theme.inherit: false // Kirigami.Theme.backgroundColor: control.Kirigami.Theme.backgroundColor visible: _filterButton.checked && _filterButton.visible width: control.width position: ToolBar.Header z: listView.z + 9 middleContent: Maui.TextField { Layout.fillWidth: true onAccepted: listModel.setFilterString(text) onTextChanged: listModel.setFilterString(text) } } Maui.Dialog { id: _removeDialog property int index title: qsTr("Remove track") message: qsTr("You can delete the file from your computer or remove it from your collection") rejectButton.text: qsTr("Delete") acceptButton.text: qsTr("Remove") page.padding: Maui.Style.space.huge onAccepted: { list.remove(listView.currentIndex) close() } onRejected: { if(Maui.FM.removeFile(list.get(index).url)) list.remove(listView.currentIndex) close() } } TableMenu { id: contextMenu MenuSeparator {} MenuItem { text: qsTr("Go to Artist") onTriggered: goToArtist() } MenuItem { text: qsTr("Go to Album") onTriggered: goToAlbum() } onFavClicked: { list.fav(listView.currentIndex, !(list.get(listView.currentIndex).fav == "1")) } onQueueClicked: Player.queueTracks([list.get(listView.currentIndex)]) onPlayClicked: quickPlayTrack(listView.currentIndex) onAppendClicked: appendTrack(listView.currentIndex) onSaveToClicked: { playlistDialog.tracks = [list.get(listView.currentIndex).url] playlistDialog.open() } onOpenWithClicked: Maui.FM.openLocation([list.get(listView.currentIndex).url]) onDeleteClicked: { _removeDialog.index= listView.currentIndex _removeDialog.open() } onRateClicked: { list.rate(listView.currentIndex, rate); } onColorClicked: { list.color(listView.currentIndex, color); } onInfoClicked: { infoView.show(list.get(listView.currentIndex)) } onCopyToClicked: { cloudView.list.upload(listView.currentIndex) } onShareClicked: { const url = list.get(listView.currentIndex).url if(isAndroid) { Maui.Android.shareDialog(url) return } _dialogLoader.sourceComponent = _shareDialogComponent root.dialog.show([url]) } } section.criteria: ViewSection.FullString section.delegate: Maui.LabelDelegate { id: _sectionDelegate label: section isSection: true width: control.width Kirigami.Theme.backgroundColor: "#333" Kirigami.Theme.textColor: "#fafafa" background: Rectangle { color: Kirigami.Theme.backgroundColor } } Maui.BaseModel { id: _tracksModel list: _tracksList recursiveFilteringEnabled: true sortCaseSensitivity: Qt.CaseInsensitive filterCaseSensitivity: Qt.CaseInsensitive } Tracks { id: _tracksList onSortByChanged: if(control.group) control.groupBy() } model: _tracksModel // property alias animBabe: delegate.animBabe delegate: TableDelegate { id: delegate width: listView.width number : trackNumberVisible ? true : false coverArt : coverArtVisible ? (control.width > 200) : coverArtVisible showQuickActions: control.showQuickActions onPressAndHold: if(Kirigami.Settings.isMobile && allowMenu) openItemMenu(index) onRightClicked: if(allowMenu) openItemMenu(index) onLeftEmblemClicked: H.addToSelection(list.get(index)) isSelected: selectionBar.contains(model.url) + sameAlbum: + { + if(coverArt) + { + if(list.get(index-1)) + { + if(list.get(index-1).album === album && list.get(index-1).artist === artist) true + else false + }else false + }else false + } + onClicked: { currentIndex = index if(selectionMode) { H.addToSelection(list.get(listView.currentIndex)) return } if(Kirigami.Settings.isMobile) rowClicked(index) } onDoubleClicked: { currentIndex = index if(!Kirigami.Settings.isMobile) rowClicked(index) } onPlay: { currentIndex = index quickPlayTrack(index) } onAppend: { currentIndex = index appendTrack(index) } onArtworkCoverClicked: { currentIndex = index goToAlbum() } Connections { target: selectionBar - onPathRemoved: + onUriRemoved: { - if(path === model.url) + if(uri === model.url) delegate.isSelected = false } - onPathAdded: + onUriAdded: { - if(path === model.url) + if(uri === model.url) delegate.isSelected = true } onCleared: delegate.isSelected = false } } function openItemMenu(index) { currentIndex = index contextMenu.rate = list.get(currentIndex).rate contextMenu.fav = list.get(currentIndex).fav == "1" contextMenu.popup() rowPressed(index) } function saveList() { var trackList = [] if(list.count > 0) { for(var i = 0; i < list.count; ++i) trackList.push(listModel.get(i).url) playlistDialog.tracks = trackList playlistDialog.open() } } function queueList() { var trackList = [] if(list.count > 0) { for(var i = 0; i < list.count; ++i) trackList.push(list.get(i)) Player.queueTracks(trackList) } } function goToAlbum() { swipeView.currentIndex = viewsIndex.albums const item = list.get(listView.currentIndex) swipeView.currentItem.item.populateTable(item.album, item.artist) contextMenu.close() } function goToArtist() { swipeView.currentIndex = viewsIndex.artists const item = list.get(listView.currentIndex) swipeView.currentItem.item.populateTable(undefined, item.artist) contextMenu.close() } function groupBy() { var prop = "undefined" if(group) switch(list.sortBy) { case Tracks.TITLE: prop = "title" break case Tracks.ARTIST: prop = "artist" break case Tracks.ALBUM: prop = "album" break case Tracks.RATE: prop = "rate" break case Tracks.FAV: prop = "fav" break case Tracks.ADDDATE: prop = "adddate" break case Tracks.RELEASEDATE: prop = "releasedate" break; case Tracks.COUNT: prop = "count" break } section.property = prop } } diff --git a/view_models/BabeTable/TableDelegate.qml b/view_models/BabeTable/TableDelegate.qml index 5f6a892..26eac49 100644 --- a/view_models/BabeTable/TableDelegate.qml +++ b/view_models/BabeTable/TableDelegate.qml @@ -1,114 +1,104 @@ import QtQuick 2.10 import QtQuick.Layouts 1.3 import QtQuick.Controls 2.10 import QtGraphicalEffects 1.0 import org.kde.kirigami 2.7 as Kirigami import org.kde.mauikit 1.0 as Maui import "../../view_models" import "../../utils/Help.js" as H Maui.ItemDelegate { id: control isCurrentItem: ListView.isCurrentItem || isSelected property bool showQuickActions: true property bool number : false property bool coverArt : false property bool showEmblem: true property bool keepEmblemOverlay: selectionMode property bool isSelected : false property color trackMood : model.color readonly property color bgColor : Kirigami.Theme.backgroundColor readonly property int altHeight : Maui.Style.rowHeight * 1.4 - readonly property bool sameAlbum : - { - if(coverArt) - { - if(list.get(index-1)) - { - if(list.get(index-1).album === album && list.get(index-1).artist === artist) true - else false - }else false - }else false - } + property bool sameAlbum : false width: parent.width height: sameAlbum ? Maui.Style.rowHeight : altHeight padding: 0 rightPadding: leftPadding leftPadding: Maui.Style.space.small signal play() signal append() signal leftClicked() signal leftEmblemClicked(int index) signal artworkCoverClicked() signal artworkCoverDoubleClicked() Kirigami.Theme.backgroundColor: model.color.length > 0 ? Qt.rgba(trackMood.r, trackMood.g, trackMood.b, 0.2): bgColor function rate(stars) { trackRating.text = stars } RowLayout { anchors.fill: parent Item { Layout.fillHeight: true Layout.preferredWidth: _leftEmblemIcon.height + Maui.Style.space.small visible: (control.keepEmblemOverlay || control.isSelected) && control.showEmblem Maui.Badge { id: _leftEmblemIcon anchors.centerIn: parent iconName: control.isSelected ? "list-remove" : "list-add" onClicked: control.leftEmblemClicked(index) size: Maui.Style.iconSizes.small } } Maui.ListItemTemplate { id: _template Layout.fillWidth: true Layout.fillHeight: true isCurrentItem: control.isCurrentItem iconSizeHint: height - Maui.Style.space.small label1.text: control.number ? model.track + ". " + model.title : model.title label2.text: model.artist + " | " + model.album label2.visible: control.coverArt ? !control.sameAlbum : true label3.text: model.fav ? (model.fav == "1" ? "\uf2D1" : "") : "" label3.font.family: "Material Design Icons" label4.font.family: "Material Design Icons" label4.text: model.rate ? H.setStars(model.rate) : "" iconVisible: !control.sameAlbum && control.coverArt imageSource: model.artwork ? model.artwork : "qrc:/assets/cover.png" ToolButton { Layout.fillHeight: true Layout.preferredWidth: implicitWidth visible: showQuickActions && (Kirigami.Settings.isMobile ? true : control.hovered) icon.name: "media-playlist-append" onClicked: control.append() opacity: control.hovered ? 0.8 : 0.6 } } } } diff --git a/widgets/PlaylistsView/PlaylistsView.qml b/widgets/PlaylistsView/PlaylistsView.qml index 13eef91..cc6ed56 100644 --- a/widgets/PlaylistsView/PlaylistsView.qml +++ b/widgets/PlaylistsView/PlaylistsView.qml @@ -1,198 +1,201 @@ import QtQuick 2.10 import QtQuick.Layouts 1.3 import QtQuick.Controls 2.10 import org.kde.kirigami 2.7 as Kirigami import org.kde.mauikit 1.0 as Maui import TracksList 1.0 +import QtGraphicalEffects 1.0 import "../../view_models/BabeTable" import "../../view_models" import "../../db/Queries.js" as Q import "../../utils/Help.js" as H Maui.Page { id: control spacing: Maui.Style.space.medium property string currentPlaylist property string playlistQuery property alias playlistModel : playlistViewModel.model property alias playlistViewList : playlistViewModel property alias listModel : filterList.listModel signal rowClicked(var track) signal quickPlayTrack(var track) signal appendTrack(var track) signal playAll() signal syncAndPlay(string playlist) signal appendAll() - footBar.rightContent: [ - ToolButton - { - id : createPlaylistBtn -// text: qsTr("Add") - icon.name : "list-add" - onClicked: newPlaylistDialog.open() - } - ] + Maui.FloatingButton + { + id: _overlayButton + z: 999 + anchors.right: parent.right + anchors.bottom: parent.bottom + anchors.margins: Maui.Style.toolBarHeight + anchors.bottomMargin: Maui.Style.toolBarHeight + icon.name : "list-add" + onClicked: newPlaylistDialog.open() + } PlaylistsViewModel { id: playlistViewModel anchors.fill: parent } Maui.NewDialog { id: newPlaylistDialog title: qsTr("New Playlist...") onFinished: addPlaylist(text) acceptText: qsTr("Create") rejectButton.visible: false } Maui.Dialog { id: _filterDialog property bool isPublic: true parent: parent maxHeight: maxWidth maxWidth: Maui.Style.unit * 600 defaultButtons: false page.padding: 0 BabeTable { id: filterList anchors.fill: parent clip: true coverArtVisible: true headBar.visible: !holder.visible title: control.currentPlaylist holder.emoji: "qrc:/assets/dialog-information.svg" holder.isMask: false holder.title : title holder.body: "Your playlist is empty,
start adding new music to it" holder.emojiSize: Maui.Style.iconSizes.huge contextMenuItems: MenuItem { text: qsTr("Remove from playlist") } // headerMenu.menuItem: [ // Maui.MenuItem // { // enabled: !playlistViewModel.model.get(playlistViewModel.currentIndex).playlistIcon // text: "Sync tags" // onTriggered: {} // }, // Maui.MenuItem // { // enabled: !playlistViewModel.model.get(playlistViewModel.currentIndex).playlistIcon // text: "Play-n-Sync" // onTriggered: // { // filterList.headerMenu.close() // syncAndPlay(playlistViewModel.currentIndex) // } // }, // Maui.MenuItem // { // enabled: !playlistViewModel.model.get(playlistViewModel.currentIndex).playlistIcon // text: "Remove playlist" // onTriggered: removePlaylist() // } // ] // contextMenu.menuItem: [ // MenuItem // { // text: qsTr("Remove from playlist") // onTriggered: // { // bae.removePlaylistTrack(filterList.model.get(filterList.currentIndex).url, playlistViewModel.model.get(playlistViewModel.currentIndex).playlist) // populate(playlistQuery) // } // } // ] section.criteria: ViewSection.FullString section.delegate: Maui.LabelDelegate { label: filterList.section.property === qsTr("stars") ? H.setStars(section) : section isSection: true labelTxt.font.family: "Material Design Icons" width: filterList.width } Connections { target: filterList onRowClicked: control.rowClicked(filterList.listModel.get(index)) onQuickPlayTrack: control.quickPlayTrack(filterList.listModel.get(filterList.currentIndex)) onAppendTrack: control.appendTrack(filterList.listModel.get(filterList.currentIndex)) onPlayAll: { if(_filterDialog.isPublic) control.syncAndPlay(control.currentPlaylist) else control.playAll() _filterDialog.close() } onAppendAll: appendAll() onPulled: populate(playlistQuery) } Connections { target: filterList.contextMenu onRemoveClicked: { playlistsList.removeTrack(playlistViewList.currentIndex, filterList.listModel.get(filterList.currentIndex).url) populate(playlistQuery) } } } } function appendToExtraList(res) { if(res.length>0) for(var i in res) playlistViewModelFilter.model.append(res[i]) } function populate(query, isPublic) { playlistQuery = query _filterDialog.isPublic = isPublic filterList.list.query = playlistQuery _filterDialog.open() } function removePlaylist() { playlistsList.removePlaylist(playlistViewList.currentIndex) } function addPlaylist(text) { var title = text.trim() if(playlistsList.insert(title)) control.listView.positionViewAtEnd() } } diff --git a/widgets/PlaylistsView/PlaylistsViewModel.qml b/widgets/PlaylistsView/PlaylistsViewModel.qml index dc1f881..4c6def6 100644 --- a/widgets/PlaylistsView/PlaylistsViewModel.qml +++ b/widgets/PlaylistsView/PlaylistsViewModel.qml @@ -1,242 +1,242 @@ import QtQuick 2.10 import QtQuick.Layouts 1.3 import QtQuick.Controls 2.10 import QtGraphicalEffects 1.13 import org.kde.kirigami 2.6 as Kirigami import org.kde.mauikit 1.0 as Maui import PlaylistsList 1.0 import TracksList 1.0 import "../../utils" import "../../view_models" import "../../db/Queries.js" as Q import "../../utils/Help.js" as H BabeList { id: control topPadding: Maui.Style.contentMargins holder.emoji: "qrc:/assets/dialog-information.svg" holder.title : qsTr("No Playlists!") holder.body: qsTr("Start creating new custom playlists") Connections { target: holder onActionTriggered: newPlaylistDialog.open() } Menu { id: _playlistMenu MenuItem { text: qsTr("Play") onTriggered: populate(Q.GET.playlistTracks_.arg(currentPlaylist), true) } MenuItem { text: qsTr("Rename") } MenuSeparator{} MenuItem { text: qsTr("Delete") Kirigami.Theme.textColor: Kirigami.Theme.negativeTextColor onTriggered: removePlaylist() } } Maui.BaseModel { id: _playlistsModel list: playlistsList } model: _playlistsModel section.criteria: ViewSection.FullString section.property: "type" section.delegate: Maui.LabelDelegate { label: "Personal" isSection: true width: control.width } delegate : Maui.ListDelegate { id: delegate width: control.width label: model.playlist Connections { target : delegate onClicked : { control.currentIndex = index currentPlaylist = playlistsList.get(index).playlist filterList.group = false populate(Q.GET.playlistTracks_.arg(currentPlaylist), true); } onRighClicked: { control.currentIndex = index currentPlaylist = playlistsList.get(index).playlist _playlistMenu.popup() } onPressAndHold: { control.currentIndex = index currentPlaylist = playlistsList.get(index).playlist _playlistMenu.popup() } } } listView.header: Rectangle { z: control.z + 999 width: control.width - height: 120 + Maui.Style.rowHeight + height: 100 + Maui.Style.rowHeight Kirigami.Theme.inherit: false Kirigami.Theme.colorSet: Kirigami.Theme.View color: Kirigami.Theme.backgroundColor ColumnLayout { anchors.fill: parent ListView { id: _defaultPlaylists Layout.fillHeight: true Layout.fillWidth: true Layout.margins: Maui.Style.space.medium spacing: Maui.Style.space.medium orientation :ListView.Horizontal model: playlistsList.defaultPlaylists() delegate: ItemDelegate { id: _delegate readonly property color m_color: modelData.color readonly property string playlist : modelData.playlist Kirigami.Theme.inherit: false Kirigami.Theme.backgroundColor: Qt.rgba(m_color.r, m_color.g, m_color.b, 0.9) Kirigami.Theme.textColor: "white" anchors.verticalCenter: parent.verticalCenter width: 200 height: parent.height * 0.9 background: Rectangle { color : Kirigami.Theme.backgroundColor radius: Maui.Style.radiusV * 2 border.color: m_color } Maui.ListItemTemplate { anchors.fill: parent iconSizeHint: Maui.Style.iconSizes.big label1.text: playlist label1.font.pointSize: Maui.Style.fontSizes.big label1.font.weight: Font.Bold label1.font.bold: true label2.text: modelData.description iconSource: modelData.icon iconVisible: true } Connections { target: _delegate onClicked: { _defaultPlaylists.currentIndex = index currentPlaylist = _delegate.playlist switch(currentPlaylist) { case "Most Played": populate(Q.GET.mostPlayedTracks, false); filterList.list.sortBy = Tracks.COUNT break; case "Rating": filterList.list.sortBy = Tracks.RATE filterList.group = true populate(Q.GET.favoriteTracks, false); break; case "Recent": populate(Q.GET.recentTracks, false); filterList.list.sortBy = Tracks.ADDDATE filterList.group = true break; case "Favs": populate(Q.GET.babedTracks, false); break; case "Online": populate(Q.GET.favoriteTracks, false); break; case "Tags": populateExtra(Q.GET.tags, "Tags") break; case "Relationships": populate(Q.GET.favoriteTracks, false); break; case "Popular": populate(Q.GET.favoriteTracks, false); break; case "Genres": populateExtra(Q.GET.genres, "Genres") break; default: break; } } } } } Item { Layout.fillWidth: true Layout.margins: Maui.Style.space.medium Layout.preferredHeight: Maui.Style.rowHeight ColorTagsBar { anchors.fill: parent onColorClicked: populate(Q.GET.colorTracks_.arg(color.toLowerCase()), true) } } } } } diff --git a/widgets/SelectionBarMenu.qml b/widgets/SelectionBar.qml similarity index 80% rename from widgets/SelectionBarMenu.qml rename to widgets/SelectionBar.qml index 5a76ce2..b72cc43 100644 --- a/widgets/SelectionBarMenu.qml +++ b/widgets/SelectionBar.qml @@ -1,220 +1,229 @@ import QtQuick 2.0 import QtQuick 2.10 import QtQuick.Controls 2.10 import QtQuick.Layouts 1.3 import "../utils" import ".." import "../utils/Help.js" as H import "../utils/Player.js" as Player import "../view_models" +import "../view_models/BabeTable" import org.kde.kirigami 2.7 as Kirigami import org.kde.mauikit 1.0 as Maui +import org.kde.mauikit 1.1 as MauiLab -Menu +MauiLab.SelectionBar { id: control width: Maui.Style.unit * 200 property int rate : 0 property string starColor : "#FFC107" property string starReg : Kirigami.Theme.textColor property string starIcon: "draw-star" signal rateClicked(int rate) + listDelegate: TableDelegate + { + Kirigami.Theme.inherit: true + width: parent.width + number: false + coverArt: true + showQuickActions: false + isSelected: true + + onLeftEmblemClicked: control.removeAtIndex(index) + + background: null + } - MenuItem + Action { - text: qsTr("Play...") + text: qsTr("Play") + icon.name: "media-playlist-play" onTriggered: { mainPlaylist.list.clear() - - var tracks = _selectionBar.selectedItems - for(var i in tracks) - Player.appendTrack(tracks[i]) - - Player.playAll() + Player.playAll(control.items) } } - MenuItem + Action { - text: qsTr("Append...") - onTriggered: Player.appendAll(_selectionBar.selectedItems) + text: qsTr("Append") + icon.name: "media-playlist-append" + onTriggered: Player.appendAll(control.items) } - MenuItem + Action { text: qsTr("Queue") + icon.name: "view-media-recent" onTriggered: { - Player.queueTracks(_selectionBar.selectedItems) - close() + Player.queueTracks(control.items) } } // MenuSeparator {} // MenuItem // { // text: qsTr("Fav/UnFav them") // onTriggered: // { // for(var i= 0; i < _selectionBar.count; i++) // _selectionBarModelList.fav(i, !(_selectionBarModelList.get(i).fav == "1")) // } // } - MenuItem + Action { - text: qsTr("Add to...") + text: qsTr("Add to") + icon.name: "document-save" onTriggered: { - playlistDialog.tracks = _selectionBar.selectedPaths + playlistDialog.tracks = control.uris playlistDialog.open() - close() } } - MenuSeparator {} - - MenuItem + Action { - text: qsTr("Share...") + text: qsTr("Share") + icon.name: "document-share" onTriggered: { if(isAndroid) { - Maui.Android.shareDialog(_selectionBar.selectedPaths) + Maui.Android.shareDialog(control.uris) return } _dialogLoader.sourceComponent = _shareDialogComponent - root.dialog.show(_selectionBar.selectedPaths) - close() + root.dialog.show(control.uris) } } - MenuSeparator {} - MenuItem + Action { text: qsTr("Remove") + icon.name: "edit-delete" Kirigami.Theme.textColor: Kirigami.Theme.negativeTextColor onTriggered: { - close() } } // MenuSeparator {} // MenuItem // { // id: starsRow // width: parent.width // height: Maui.Style.iconSizes.medium + Maui.Style.space.small // RowLayout // { // anchors.fill: parent // ToolButton // { // Layout.fillWidth: true // Layout.fillHeight: true // icon.name: starIcon // icon.width: Maui.Style.iconSizes.medium // icon.color: rate >= 1 ? starColor :starReg // onClicked: // { // rate = 1 // } // } // ToolButton // { // Layout.fillWidth: true // Layout.fillHeight: true // icon.width: Maui.Style.iconSizes.medium // icon.name: starIcon // icon.color: rate >= 2 ? starColor :starReg // onClicked: // { // rate = 2 // } // } // ToolButton // { // Layout.fillWidth: true // Layout.fillHeight: true // icon.width: Maui.Style.iconSizes.medium // icon.name: starIcon // icon.color: rate >= 3 ? starColor :starReg // onClicked: // { // rate = 3 // } // } // ToolButton // { // Layout.fillWidth: true // Layout.fillHeight: true // icon.width: Maui.Style.iconSizes.medium // icon.name: starIcon // icon.color: rate >= 4 ? starColor :starReg // onClicked: // { // rate = 4 // } // } // ToolButton // { // Layout.fillWidth: true // Layout.fillHeight: true // icon.width: Maui.Style.iconSizes.medium // icon.name: starIcon // icon.color: rate >= 5 ? starColor :starReg // onClicked: // { // rate = 5 // } // } // } // } // onRateChanged: // { // close() // for(var i= 0; i < _selectionBar.count; i++) // _selectionBarModelList.rate(i, control.rate) // } // MenuItem // { // id: colorsRow // width: parent.width // height: Maui.Style.iconSizes.medium + Maui.Style.space.small // ColorTagsBar // { // anchors.fill: parent // onColorClicked: // { // for(var i= 0; i < _selectionBar.count; i++) // _selectionBarModelList.color(i, color) // control.close() // } // } // } }