diff --git a/src/qtquick/qml/EntryDetails.qml b/src/qtquick/qml/EntryDetails.qml index d60c1acf..0c4a9320 100644 --- a/src/qtquick/qml/EntryDetails.qml +++ b/src/qtquick/qml/EntryDetails.qml @@ -1,231 +1,215 @@ /* * Copyright (C) 2019 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ /** * @brief A Kirigami.Page component used for displaying the details for a single entry * * This component is equivalent to the details view in the old DownloadDialog * @see KNewStuff::DownloadDialog * @since 5.63 */ import QtQuick 2.11 import QtQuick.Controls 2.11 as QtControls import QtQuick.Layouts 1.11 as QtLayouts import org.kde.kirigami 2.7 as Kirigami import org.kde.kcm 1.2 as KCM import org.kde.newstuff 1.62 as NewStuff import "private" as Private KCM.SimpleKCM { id: component property QtObject newStuffModel property int index property string name property var author property alias shortSummary: shortSummaryItem.text property alias summary: summaryItem.text; property alias previews: screenshotsItem.screenshotsModel property string homepage property string donationLink property int status property int commentsCount property int rating property int downloadCount property var downloadLinks property string providerId NewStuff.DownloadItemsSheet { id: downloadItemsSheet onItemPicked: { var entryName = newStuffModel.data(newStuffModel.index(entryId, 0), NewStuff.ItemsModel.NameRole); applicationWindow().showPassiveNotification(i18nc("A passive notification shown when installation of an item is initiated", "Installing %1 from %2").arg(downloadName).arg(entryName), 1500); newStuffModel.installItem(entryId, downloadItemId); } } Connections { target: newStuffModel onEntryChanged: { var status = newStuffModel.data(newStuffModel.index(index, 0), NewStuff.ItemsModel.StatusRole); if (status == NewStuff.ItemsModel.DownloadableStatus || status == NewStuff.ItemsModel.InstalledStatus || status == NewStuff.ItemsModel.UpdateableStatus || status == NewStuff.ItemsModel.DeletedStatus) { statusCard.message = ""; } else if (status == NewStuff.ItemsModel.InstallingStatus) { statusCard.message = i18nc("Status message to be shown when the entry is in the process of being installed", "Currently installing the item %1 by %2. Please wait...").arg(component.name).arg(entryAuthor.name); } else if (status == NewStuff.ItemsModel.UpdatingStatus) { statusCard.message = i18nc("Status message to be shown when the entry is in the process of being updated", "Currently updating the item %1 by %2. Please wait...").arg(component.name).arg(entryAuthor.name); } else { statusCard.message = i18nc("Status message which should only be shown when the entry has been given some unknown or invalid status.", "This item is currently in an invalid or unknown state. Please report this to the KDE Community in a bug report."); } } } NewStuff.Author { id: entryAuthor engine: component.newStuffModel.engine providerId: component.providerId username: author.name } title: i18nc("Combined title for the entry details page made of the name of the entry, and the author's name", "%1 by %2").arg(component.name).arg(entryAuthor.name) - titleDelegate: QtLayouts.RowLayout { - implicitHeight: title.height - Kirigami.Heading { - id: title - level: 1 - - QtLayouts.Layout.fillWidth: true; - QtLayouts.Layout.preferredWidth: titleTextMetrics.width - QtLayouts.Layout.minimumWidth: titleTextMetrics.width - opacity: component.isCurrentPage ? 1 : 0.4 - maximumLineCount: 1 - elide: Text.ElideRight - text: component.title - TextMetrics { - id: titleTextMetrics - text: component.title - font: title.font - } - } - QtControls.ToolButton { - text: component.downloadCount == 1 ? i18nc("Request installation of this item, available when there is exactly one downloadable item", "Install") : i18nc("Show installation options, where there is more than one downloadable item", "Install..."); - icon.name: "install" - onClicked: { - if (component.downloadCount == 1) { - newStuffModel.installItem(component.index); - } else { - downloadItemsSheet.downloadLinks = component.downloadLinks; - downloadItemsSheet.entryId = component.index; - downloadItemsSheet.open(); + actions { + contextualActions: [ + Kirigami.Action { + text: component.downloadCount == 1 ? i18nc("Request installation of this item, available when there is exactly one downloadable item", "Install") : i18nc("Show installation options, where there is more than one downloadable item", "Install..."); + icon.name: "install" + onTriggered: { + if (component.downloadCount == 1) { + newStuffModel.installItem(component.index); + } else { + downloadItemsSheet.downloadLinks = component.downloadLinks; + downloadItemsSheet.entryId = component.index; + downloadItemsSheet.open(); + } } + enabled: component.status == NewStuff.ItemsModel.DownloadableStatus || component.status == NewStuff.ItemsModel.DeletedStatus; + visible: enabled; + }, + Kirigami.Action { + text: i18nc("Request updating of this item", "Update"); + icon.name: "update" + onTriggered: { newStuffModel.installItem(component.index); } + enabled: component.status == NewStuff.ItemsModel.UpdateableStatus; + visible: enabled; + }, + Kirigami.Action { + text: i18nc("Request uninstallation of this item", "Uninstall"); + icon.name: "uninstall" + onTriggered: { newStuffModel.uninstallItem(component.index); } + enabled: component.status == NewStuff.ItemsModel.InstalledStatus + visible: enabled; } - enabled: component.status == NewStuff.ItemsModel.DownloadableStatus || component.status == NewStuff.ItemsModel.DeletedStatus; - visible: enabled; - } - QtControls.ToolButton { - text: i18nc("Request updating of this item", "Update"); - icon.name: "update" - onClicked: { newStuffModel.installItem(component.index); } - enabled: component.status == NewStuff.ItemsModel.UpdateableStatus; - visible: enabled; - } - QtControls.ToolButton { - text: i18nc("Request uninstallation of this item", "Uninstall"); - icon.name: "uninstall" - onClicked: { newStuffModel.uninstallItem(component.index); } - enabled: component.status == NewStuff.ItemsModel.InstalledStatus - visible: enabled; - } + ] } QtLayouts.ColumnLayout { spacing: Kirigami.Units.smallSpacing Item { width: parent.width; height: Kirigami.Units.gridUnit * 3; } Kirigami.AbstractCard { id: statusCard property string message; visible: opacity > 0 opacity: message.length > 0 ? 1 : 0 Behavior on opacity { NumberAnimation { duration: Kirigami.Units.longDuration; } } QtLayouts.Layout.fillWidth: true QtLayouts.Layout.margins: Kirigami.Units.largeSpacing Item { id: statusContent implicitHeight: statusCard.message.length > 0 ? Math.max(statusBusy.height, statusLabel.height) + Kirigami.Units.largeSpacing * 4 : 0 implicitWidth: statusCard.width QtControls.BusyIndicator { id: statusBusy anchors { top: parent.top left: parent.left } running: statusCard.opacity > 0 Rectangle { anchors.fill: parent; color: "red"; opacity: 0.3; } } QtControls.Label { id: statusLabel anchors { top: parent.top left: statusBusy.right leftMargin: Kirigami.Units.largeSpacing right: parent.right } text: statusCard.message wrapMode: Text.Wrap Rectangle { anchors.fill: parent; color: "blue"; opacity: 0.3; } } } } Private.EntryScreenshots { id: screenshotsItem QtLayouts.Layout.fillWidth: true } Kirigami.Heading { id: shortSummaryItem QtLayouts.Layout.fillWidth: true } Kirigami.FormLayout { QtLayouts.Layout.fillWidth: true Kirigami.LinkButton { Kirigami.FormData.label: i18n("Comments and Reviews:") enabled: component.commentsCount > 0 text: i18nc("A link which, when clicked, opens a new sub page with reviews (comments) for this entry", "%1 Reviews").arg(component.commentsCount) onClicked: pageStack.push(commentsPage) } Private.Rating { id: ratingsItem Kirigami.FormData.label: i18n("Rating:") rating: Math.floor(component.rating / 10) } Kirigami.LinkButton { Kirigami.FormData.label: i18n("Homepage:") text: i18nc("A link which, when clicked, opens the website associated with the entry (this could be either one specific to the project, the author's homepage, or any other website they have chosen for the purpose)", "Open the homepage for %2").arg(component.name) onClicked: Qt.openUrlExternally(component.homepage) } Kirigami.LinkButton { Kirigami.FormData.label: i18n("How To Donate:") text: i18nc("A link which, when clicked, opens a website with information on donation in support of the entry", "Find out how to donate to this project") onClicked: Qt.openUrlExternally(component.donationLink) } } QtControls.Label { id: summaryItem QtLayouts.Layout.fillWidth: true QtLayouts.Layout.margins: Kirigami.Units.largeSpacing wrapMode: Text.Wrap } } Component { id: commentsPage Private.EntryCommentsPage { itemsModel: component.newStuffModel entryIndex: component.index entryName: component.name entryAuthorId: component.author.name entryProviderId: component.providerId } } } diff --git a/src/qtquick/qml/Page.qml b/src/qtquick/qml/Page.qml index cf4c5590..f042d9d1 100644 --- a/src/qtquick/qml/Page.qml +++ b/src/qtquick/qml/Page.qml @@ -1,238 +1,232 @@ /* * Copyright (C) 2019 Dan Leinir Turthra Jensen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ /** * @brief A Kirigami.Page component used for managing KNS entries * * This component is functionally equivalent to the old DownloadDialog * @see KNewStuff::DownloadDialog * @since 5.63 */ import QtQuick 2.11 import QtQuick.Controls 2.11 as QtControls import QtQuick.Layouts 1.11 as QtLayouts import QtGraphicalEffects 1.11 as QtEffects import org.kde.kcm 1.2 as KCM import org.kde.kirigami 2.7 as Kirigami import org.kde.newstuff 1.62 as NewStuff import "private" as Private import "private/entrygriddelegates" as EntryGridDelegates KCM.GridViewKCM { id: root; /** * @brief The configuration file which describes the application (knsrc) * * The format and location of this file is found in the documentation for * KNS3::DownloadDialog */ property alias configFile: newStuffEngine.configFile; readonly property alias engine: newStuffEngine; /** * Any generic message from the NewStuff.Engine * @param message The message to be shown to the user */ signal message(string message); /** * A message posted usually describing that whatever action a recent busy * message said was happening has been completed * @param message The message to be shown to the user */ signal idleMessage(string message); /** * A message posted when the engine is busy doing something long duration * (usually this will be when fetching installation data) * @param message The message to be shown to the user */ signal busyMessage(string message); /** * A message posted when something has gone wrong * @param message The message to be shown to the user */ signal errorMessage(string message); property string uninstallLabel: i18nc("Request uninstallation of this item", "Uninstall"); property string useLabel: i18nc("If a knsrc file defines an adoption command, the option to run this command and 'use' an item becomes available. This is the text for an action to do so.", "Use"); property int viewMode: Page.ViewMode.Tiles enum ViewMode { Tiles, Icons, Preview } title: newStuffEngine.name NewStuff.Engine { id: newStuffEngine; onMessage: root.message(message); onIdleMessage: root.idleMessage(message); onBusyMessage: root.busyMessage(message); onErrorMessage: root.errorMessage(message); } NewStuff.QuestionAsker {} titleDelegate: QtLayouts.RowLayout { + QtLayouts.Layout.fillWidth: true Kirigami.Heading { id: title level: 1 QtLayouts.Layout.fillWidth: true; - QtLayouts.Layout.preferredWidth: titleTextMetrics.width - QtLayouts.Layout.minimumWidth: titleTextMetrics.width opacity: root.isCurrentPage ? 1 : 0.4 maximumLineCount: 1 elide: Text.ElideRight text: root.title - TextMetrics { - id: titleTextMetrics - text: root.title - font: title.font - } } QtControls.ButtonGroup { id: displayModeGroup buttons: [displayModeTiles, displayModeIcons] } QtControls.ToolButton { id: displayModeTiles icon.name: "view-list-details" onClicked: { root.viewMode = Page.ViewMode.Tiles; } checked: root.viewMode == Page.ViewMode.Tiles } QtControls.ToolButton { id: displayModeIcons icon.name: "view-list-icons" onClicked: { root.viewMode = Page.ViewMode.Icons; } checked: root.viewMode == Page.ViewMode.Icons } QtControls.ToolButton { id: displayPreview icon.name: "view-preview" onClicked: { root.viewMode = Page.ViewMode.Preview; } checked: root.viewMode == Page.ViewMode.Preview } Kirigami.ActionTextField { id: searchField placeholderText: i18n("Search...") focusSequence: "Ctrl+F" rightActions: [ Kirigami.Action { iconName: "edit-clear" visible: searchField.text !== "" onTriggered: { searchField.text = ""; searchField.accepted(); } } ] onAccepted: { newStuffEngine.searchTerm = searchField.text; } } } footer: QtLayouts.RowLayout { QtControls.ComboBox { id: categoriesCombo QtLayouts.Layout.fillWidth: true model: newStuffEngine.categories textRole: "displayName" onCurrentIndexChanged: { newStuffEngine.categoriesFilter = model.data(model.index(currentIndex, 0), NewStuff.CategoriesModel.NameRole); } } QtControls.ComboBox { id: filterCombo QtLayouts.Layout.fillWidth: true model: ListModel {} Component.onCompleted: { filterCombo.model.append({ text: i18nc("List option which will set the filter to show everything", "Show Everything") }); filterCombo.model.append({ text: i18nc("List option which will set the filter so only installed items are shown", "Installed Only") }); filterCombo.model.append({ text: i18nc("List option which will set the filter so only installed items with updates available are shown", "Updateable Only") }); filterCombo.currentIndex = newStuffEngine.filter; } onCurrentIndexChanged: { newStuffEngine.filter = currentIndex; } } QtControls.ComboBox { id: sortCombo QtLayouts.Layout.fillWidth: true model: ListModel { } Component.onCompleted: { sortCombo.model.append({ text: i18nc("List option which will set the sort order to based on when items were most recently updated", "Show most recent first") }); sortCombo.model.append({ text: i18nc("List option which will set the sort order to be alphabetical based on the name", "Sort alphabetically") }); sortCombo.model.append({ text: i18nc("List option which will set the sort order to based on user ratings", "Show highest rated first") }); sortCombo.model.append({ text: i18nc("List option which will set the sort order to based on number of downloads", "Show most downloaded first") }); sortCombo.currentIndex = newStuffEngine.sortOrder; } onCurrentIndexChanged: { newStuffEngine.sortOrder = currentIndex; } } } view.model: NewStuff.ItemsModel { id: newStuffModel; engine: newStuffEngine; } NewStuff.DownloadItemsSheet { id: downloadItemsSheet onItemPicked: { var entryName = newStuffModel.data(newStuffModel.index(entryId, 0), NewStuff.ItemsModel.NameRole); applicationWindow().showPassiveNotification(i18nc("A passive notification shown when installation of an item is initiated", "Installing %1 from %2").arg(downloadName).arg(entryName), 1500); newStuffModel.installItem(entryId, downloadItemId); } } view.implicitCellWidth: root.viewMode == Page.ViewMode.Tiles ? Kirigami.Units.gridUnit * 30 : (root.viewMode == Page.ViewMode.Preview ? Kirigami.Units.gridUnit * 25 : Kirigami.Units.gridUnit * 10) view.implicitCellHeight: root.viewMode == Page.ViewMode.Tiles ? Math.round(view.implicitCellWidth / 3) : (root.viewMode == Page.ViewMode.Preview ? Kirigami.Units.gridUnit * 25 : Math.round(view.implicitCellWidth / 1.6) + Kirigami.Units.gridUnit*2) view.delegate: root.viewMode == Page.ViewMode.Tiles ? tileDelegate : (root.viewMode == Page.ViewMode.Preview ? bigPreviewDelegate : thumbDelegate) Component { id: bigPreviewDelegate EntryGridDelegates.BigPreviewDelegate { } } Component { id: tileDelegate EntryGridDelegates.TileDelegate { useLabel: root.useLabel uninstallLabel: root.uninstallLabel } } Component { id: thumbDelegate EntryGridDelegates.ThumbDelegate { useLabel: root.useLabel uninstallLabel: root.uninstallLabel } } Component { id: detailsPage; NewStuff.EntryDetails { } } }