/** * SPDX-FileCopyrightText: 2020 Tobias Fella * SPDX-FileCopyrightText: 2021 Bart De Vries * * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL */ import QtQuick import QtQuick.Controls as Controls import QtQuick.Layouts import QtQuick.Effects import QtQml.Models import org.kde.kirigami as Kirigami import org.kde.kasts Controls.ItemDelegate { id: feedDelegate property var countProperty: (kastsMainWindow.feedSorting === FeedsProxyModel.UnreadDescending || kastsMainWindow.feedSorting === FeedsProxyModel.UnreadAscending) ? feed.unreadEntryCount : ((kastsMainWindow.feedSorting === FeedsProxyModel.NewDescending || kastsMainWindow.feedSorting === FeedsProxyModel.NewAscending) ? feed.newEntryCount : ((kastsMainWindow.feedSorting === FeedsProxyModel.FavoriteDescending || kastsMainWindow.feedSorting === FeedsProxyModel.FavoriteAscending) ? feed.favoriteEntryCount : 0)) property int cardSize: 0 property int cardMargin: 0 property int borderWidth: 1 implicitWidth: cardSize + 2 * cardMargin implicitHeight: cardSize + 2 * cardMargin property QtObject listView: undefined property bool selected: false property bool isCurrentItem: false // to restore currentItem when model is resorted property string currentItemUrl: "" property int row: model ? model.row : -1 property var activeBackgroundColor: Qt.lighter(Kirigami.Theme.highlightColor, 1.3) highlighted: selected Accessible.role: Accessible.Button Accessible.name: feed.name Accessible.onPressAction: { clicked(); } Keys.onReturnPressed: clicked() // We need to update the "selected" status: // - if the selected indexes changes // - if our delegate moves // - if the model moves and the delegate stays in the same place function updateIsSelected() { selected = listView.selectionModel.rowIntersectsSelection(row); } onRowChanged: { updateIsSelected(); } Component.onCompleted: { updateIsSelected(); } background: Rectangle { // Background for highlighted / hovered / active items Rectangle { id: background anchors.fill: parent color: feedDelegate.checked || feedDelegate.highlighted || (feedDelegate.supportsMouseEvents && feedDelegate.pressed) ? feedDelegate.activeBackgroundColor : Kirigami.Theme.backgroundColor Rectangle { id: internal property bool indicateActiveFocus: feedDelegate.pressed || Kirigami.Settings.tabletMode || feedDelegate.activeFocus || (feedDelegate.ListView.view ? feedDelegate.ListView.view.activeFocus : false) anchors.fill: parent visible: !Kirigami.Settings.tabletMode && feedDelegate.hoverEnabled color: feedDelegate.activeBackgroundColor opacity: (feedDelegate.hovered || feedDelegate.highlighted || feedDelegate.activeFocus) && !feedDelegate.pressed ? 0.5 : 0 } } Kirigami.ShadowedRectangle { anchors.fill: parent anchors.margins: cardMargin anchors.leftMargin: cardMargin color: Kirigami.Theme.backgroundColor radius: Kirigami.Units.smallSpacing shadow.size: Kirigami.Units.largeSpacing shadow.color: Qt.rgba(0.0, 0.0, 0.0, 0.15) shadow.yOffset: borderWidth * 2 border.width: borderWidth border.color: Qt.tint(Kirigami.Theme.textColor, Qt.rgba(color.r, color.g, color.b, 0.6)) } } contentItem: MouseArea { id: mouseArea anchors.fill: parent anchors.margins: cardMargin + borderWidth implicitWidth: cardSize - 2 * borderWidth implicitHeight: cardSize - 2 * borderWidth acceptedButtons: Qt.LeftButton | Qt.RightButton onClicked: (mouse) => { // Keep track of (currently) selected items var modelIndex = feedDelegate.listView.model.index(index, 0); if (feedDelegate.listView.selectionModel.isSelected(modelIndex) && mouse.button == Qt.RightButton) { feedDelegate.listView.contextMenu.popup(null, mouse.x+1, mouse.y+1); } else if (mouse.modifiers & Qt.ShiftModifier) { // Have to take a detour through c++ since selecting large sets // in QML is extremely slow feedDelegate.listView.selectionModel.select(feedDelegate.listView.model.createSelection(modelIndex.row, feedDelegate.listView.selectionModel.currentIndex.row), ItemSelectionModel.ClearAndSelect | ItemSelectionModel.Rows); } else if (mouse.modifiers & Qt.ControlModifier) { feedDelegate.listView.selectionModel.select(modelIndex, ItemSelectionModel.Toggle | ItemSelectionModel.Rows); } else if (mouse.button == Qt.LeftButton) { feedDelegate.listView.currentIndex = index; feedDelegate.listView.selectionModel.setCurrentIndex(modelIndex, ItemSelectionModel.ClearAndSelect | ItemSelectionModel.Rows); feedDelegate.clicked(); } else if (mouse.button == Qt.RightButton) { // This item is right-clicked, but isn't selected feedDelegate.listView.selectionForContextMenu = [modelIndex]; feedDelegate.listView.contextMenu.popup(null, mouse.x+1, mouse.y+1); } } onPressAndHold: { var modelIndex = feedDelegate.listView.model.index(index, 0); feedDelegate.listView.selectionModel.select(modelIndex, ItemSelectionModel.Toggle | ItemSelectionModel.Rows); } Connections { target: listView.selectionModel function onSelectionChanged() { updateIsSelected(); } } Connections { target: listView.model function onLayoutAboutToBeChanged() { if (feedList.currentItem === feedDelegate) { isCurrentItem = true; currentItemUrl = feed.url; } else { isCurrentItem = false; currentItemUrl = ""; } } function onLayoutChanged() { updateIsSelected(); if (isCurrentItem) { // yet another hack because "index" is still giving the old // value here; so we have to manually find the new index. for (var i = 0; i < feedList.model.rowCount(); i++) { if (feedList.model.data(feedList.model.index(i, 0), FeedsModel.UrlRole) == currentItemUrl) { feedList.currentIndex = i; } } } } } ImageWithFallback { id: img anchors.fill: parent imageSource: feed.cachedImage imageTitle: feed.name isLoading: feed.refreshing absoluteRadius: Kirigami.Units.smallSpacing - borderWidth } Rectangle { id: countRectangle visible: countProperty > 0 anchors.top: img.top anchors.right: img.right width: actionsButton.width height: actionsButton.height color: feedDelegate.activeBackgroundColor radius: Kirigami.Units.smallSpacing - 2 * borderWidth } Controls.Label { id: countLabel visible: countProperty > 0 anchors.centerIn: countRectangle anchors.margins: Kirigami.Units.smallSpacing text: countProperty font.bold: true color: Kirigami.Theme.highlightedTextColor } Rectangle { id: actionsRectangle anchors.fill: actionsButton color: "black" opacity: 0.5 radius: Kirigami.Units.smallSpacing - 2 * borderWidth } Controls.Button { id: actionsButton anchors.right: img.right anchors.bottom: img.bottom anchors.margins: 0 padding: 0 flat: true icon.name: "overflow-menu" icon.color: "white" onClicked: actionOverlay.open() } } onClicked: { lastFeed = feed.url if (pageStack.depth > 1) pageStack.pop(); pageStack.push("qrc:/qt/qml/org/kde/kasts/qml/FeedDetailsPage.qml", {"feed": feed}) } Controls.ToolTip.visible: hovered Controls.ToolTip.text: feed.name Controls.ToolTip.delay: Kirigami.Units.toolTipDelay Kirigami.MenuDialog { id: actionOverlay // parent: applicationWindow().overlay showCloseButton: true title: feed.name actions: [ Kirigami.Action { onTriggered: { while (pageStack.depth > 1) pageStack.pop() pageStack.push("qrc:/qt/qml/org/kde/kasts/qml/FeedDetailsPage.qml", {"feed": feed}); } icon.name: "documentinfo" text: i18n("Podcast Details") }, Kirigami.Action { onTriggered: { if (feed.url === lastFeed) while(pageStack.depth > 1) pageStack.pop() DataManager.removeFeed(feed) } icon.name: "delete" text: i18n("Remove Podcast") } ] } }