Warning, /frameworks/knewstuff/src/qtquick/qml/Page.qml is written in an unsupported language. File is not indexed.
0001 /* 0002 SPDX-FileCopyrightText: 2019 Dan Leinir Turthra Jensen <admin@leinir.dk> 0003 0004 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0005 */ 0006 0007 /** 0008 * @brief A Kirigami.Page component used for managing KNS entries 0009 * 0010 * This component is functionally equivalent to the old DownloadDialog 0011 * @see KNewStuff::DownloadDialog 0012 * @since 5.63 0013 */ 0014 0015 import QtQuick 2.11 0016 import QtQuick.Controls 2.11 as QtControls 0017 import QtQuick.Layouts 1.11 as QtLayouts 0018 import QtGraphicalEffects 1.11 as QtEffects 0019 0020 import org.kde.kcm 1.2 as KCM 0021 import org.kde.kirigami 2.19 as Kirigami 0022 0023 import org.kde.newstuff 1.85 as NewStuff 0024 0025 import "private" as Private 0026 import "private/entrygriddelegates" as EntryGridDelegates 0027 0028 KCM.GridViewKCM { 0029 id: root; 0030 /** 0031 * @brief The configuration file which describes the application (knsrc) 0032 * 0033 * The format and location of this file is found in the documentation for 0034 * KNS3::DownloadDialog 0035 */ 0036 property alias configFile: newStuffEngine.configFile; 0037 readonly property alias engine: newStuffEngine; 0038 0039 /** 0040 * Any generic message from the NewStuff.Engine 0041 * @param message The message to be shown to the user 0042 */ 0043 signal message(string message); 0044 /** 0045 * A message posted usually describing that whatever action a recent busy 0046 * message said was happening has been completed 0047 * @param message The message to be shown to the user 0048 */ 0049 signal idleMessage(string message); 0050 /** 0051 * A message posted when the engine is busy doing something long duration 0052 * (usually this will be when fetching installation data) 0053 * @param message The message to be shown to the user 0054 */ 0055 signal busyMessage(string message); 0056 /** 0057 * A message posted when something has gone wrong 0058 * @param message The message to be shown to the user 0059 */ 0060 signal errorMessage(string message); 0061 0062 /** 0063 * Whether or not to show the Upload... context action 0064 * Usually this will be bound to the engine's property which usually defines 0065 * this, but you can override it programmatically by setting it here. 0066 * @since 5.85 0067 * @see KNSCore::Engine::uploadEnabled 0068 */ 0069 property alias showUploadAction: uploadAction.visible 0070 0071 0072 /** 0073 * Show the details page for a specific entry. 0074 * If you call this function before the engine initialisation has been completed, 0075 * the action itself will be postponed until that has happened. 0076 * @param providerId The provider ID for the entry you wish to show details for 0077 * @param entryId The unique ID for the entry you wish to show details for 0078 * @since 5.79 0079 */ 0080 function showEntryDetails(providerId, entryId) { 0081 _showEntryDetailsThrottle.providerId = providerId; 0082 _showEntryDetailsThrottle.entryId = entryId; 0083 newStuffEngine.engine.storeSearch(); 0084 0085 //check if entry in question is perhaps a group, if so, load the new details. 0086 var theIndex = newStuffModel.indexOfEntryId(providerId, entryId); 0087 var type = newStuffModel.data(newStuffModel.index(theIndex, 0), NewStuff.ItemsModel.EntryTypeRole); 0088 0089 if (type === NewStuff.ItemsModel.GroupEntry) { 0090 newStuffEngine.searchTerm = newStuffModel.data(newStuffModel.index(theIndex, 0), NewStuff.ItemsModel.PayloadRole); 0091 } else { 0092 newStuffEngine.engine.fetchEntryById(entryId); 0093 } 0094 0095 if (newStuffEngine.isLoading) { 0096 _showEntryDetailsThrottle.enabled = true; 0097 } else { 0098 _showEntryDetailsThrottle.onIsLoadingDataChanged(); 0099 } 0100 0101 } 0102 Connections { 0103 id: _showEntryDetailsThrottle; 0104 target: newStuffModel; 0105 enabled: false; 0106 property var entryId; 0107 property var providerId; 0108 function onIsLoadingDataChanged() { 0109 if (newStuffModel.isLoadingData === false && root.view.count == 1) { 0110 _showEntryDetailsThrottle.enabled = false; 0111 var theIndex = newStuffModel.indexOfEntryId(_showEntryDetailsThrottle.providerId, _showEntryDetailsThrottle.entryId); 0112 if (theIndex > -1) { 0113 pageStack.push(detailsPage, { 0114 newStuffModel: newStuffModel, 0115 index: theIndex, 0116 name: newStuffModel.data(newStuffModel.index(theIndex, 0), NewStuff.ItemsModel.NameRole), 0117 author: newStuffModel.data(newStuffModel.index(theIndex, 0), NewStuff.ItemsModel.AuthorRole), 0118 previews: newStuffModel.data(newStuffModel.index(theIndex, 0), NewStuff.ItemsModel.PreviewsRole), 0119 shortSummary: newStuffModel.data(newStuffModel.index(theIndex, 0), NewStuff.ItemsModel.ShortSummaryRole), 0120 summary: newStuffModel.data(newStuffModel.index(theIndex, 0), NewStuff.ItemsModel.SummaryRole), 0121 homepage: newStuffModel.data(newStuffModel.index(theIndex, 0), NewStuff.ItemsModel.HomepageRole), 0122 donationLink: newStuffModel.data(newStuffModel.index(theIndex, 0), NewStuff.ItemsModel.DonationLinkRole), 0123 status: newStuffModel.data(newStuffModel.index(theIndex, 0), NewStuff.ItemsModel.StatusRole), 0124 commentsCount: newStuffModel.data(newStuffModel.index(theIndex, 0), NewStuff.ItemsModel.NumberOfCommentsRole), 0125 rating: newStuffModel.data(newStuffModel.index(theIndex, 0), NewStuff.ItemsModel.RatingRole), 0126 downloadCount: newStuffModel.data(newStuffModel.index(theIndex, 0), NewStuff.ItemsModel.DownloadCountRole), 0127 downloadLinks: newStuffModel.data(newStuffModel.index(theIndex, 0), NewStuff.ItemsModel.DownloadLinksRole), 0128 providerId: _showEntryDetailsThrottle.providerId 0129 }); 0130 _restoreSearchState.enabled = true; 0131 } else { 0132 root.message(i18ndc("knewstuff5", "A message which is shown when the user attempts to display a specific entry from a specific provider, but that entry isn't found", "The entry you attempted to display, identified by the unique ID %1, could not be found.", _showEntryDetailsThrottle.entryId)); 0133 newStuffEngine.engine.restoreSearch(); 0134 } 0135 } else if (newStuffModel.isLoadingData === false && root.view.count > 1) { 0136 // right now, this is only one level deep... 0137 _showEntryDetailsThrottle.enabled = false; 0138 _restoreSearchState.enabled = true; 0139 } 0140 } 0141 } 0142 Connections { 0143 id: _restoreSearchState; 0144 target: pageStack; 0145 enabled: false; 0146 function onCurrentIndexChanged() { 0147 if (pageStack.currentIndex === 0) { 0148 newStuffEngine.engine.restoreSearch(); 0149 _restoreSearchState.enabled = false; 0150 } 0151 } 0152 } 0153 0154 property string uninstallLabel: i18ndc("knewstuff5", "Request uninstallation of this item", "Uninstall"); 0155 property string useLabel: engine.engine.useLabel 0156 0157 property int viewMode: Page.ViewMode.Tiles 0158 enum ViewMode { 0159 Tiles, 0160 Icons, 0161 Preview 0162 } 0163 0164 // Otherwise the first item will be focused, see BUG: 424894 0165 Component.onCompleted: { 0166 view.currentIndex = -1 0167 } 0168 0169 title: newStuffEngine.name 0170 0171 view.header: Item { 0172 implicitWidth: view.width - Kirigami.Units.gridUnit 0173 implicitHeight: Kirigami.Units.gridUnit * 3 0174 visible: !loadingOverlay.visible 0175 Kirigami.InlineMessage { 0176 anchors.fill: parent 0177 anchors.margins: Kirigami.Units.smallSpacing 0178 visible: true 0179 text: i18nd("knewstuff5", "The content available here has been uploaded by users like you, and has not been reviewed by your distributor for functionality or stability.") 0180 } 0181 } 0182 0183 NewStuff.Engine { 0184 id: newStuffEngine; 0185 property string statusMessage; 0186 onMessage: { 0187 root.message(message); 0188 statusMessage = message; 0189 } 0190 onIdleMessage: { 0191 root.idleMessage(message); 0192 statusMessage = message; 0193 } 0194 onBusyMessage: { 0195 root.busyMessage(message); 0196 statusMessage = message; 0197 } 0198 onErrorMessage: { 0199 root.errorMessage(message); 0200 statusMessage = message; 0201 } 0202 } 0203 NewStuff.QuestionAsker {} 0204 Private.ErrorDisplayer { engine: newStuffEngine; active: root.isCurrentPage; } 0205 0206 QtControls.ActionGroup { id: viewModeActionGroup } 0207 QtControls.ActionGroup { id: viewFilterActionGroup } 0208 QtControls.ActionGroup { id: viewSortingActionGroup } 0209 actions { 0210 contextualActions: [ 0211 Kirigami.Action { 0212 text: { 0213 if (root.viewMode == Page.ViewMode.Tiles) { 0214 return i18nd("knewstuff5", "Tiles"); 0215 } else if (root.viewMode == Page.ViewMode.Icons) { 0216 return i18nd("knewstuff5", "Icons"); 0217 } else { 0218 return i18nd("knewstuff5", "Preview"); 0219 } 0220 } 0221 checkable: false 0222 icon.name: { 0223 if (root.viewMode == Page.ViewMode.Tiles) { 0224 return "view-list-details"; 0225 } else if (root.viewMode == Page.ViewMode.Icons) { 0226 return "view-list-icons"; 0227 } else { 0228 return "view-preview"; 0229 } 0230 } 0231 Kirigami.Action { 0232 icon.name: "view-list-details" 0233 text: i18nd("knewstuff5", "Detailed Tiles View Mode") 0234 onTriggered: { root.viewMode = Page.ViewMode.Tiles; } 0235 checked: root.viewMode == Page.ViewMode.Tiles 0236 checkable: true 0237 QtControls.ActionGroup.group: viewModeActionGroup 0238 } 0239 Kirigami.Action { 0240 icon.name: "view-list-icons" 0241 text: i18nd("knewstuff5", "Icons Only View Mode") 0242 onTriggered: { root.viewMode = Page.ViewMode.Icons; } 0243 checked: root.viewMode == Page.ViewMode.Icons 0244 checkable: true 0245 QtControls.ActionGroup.group: viewModeActionGroup 0246 } 0247 Kirigami.Action { 0248 icon.name: "view-preview" 0249 text: i18nd("knewstuff5", "Large Preview View Mode") 0250 onTriggered: { root.viewMode = Page.ViewMode.Preview; } 0251 checked: root.viewMode == Page.ViewMode.Preview 0252 checkable: true 0253 QtControls.ActionGroup.group: viewModeActionGroup 0254 } 0255 }, 0256 Kirigami.Action { 0257 text: { 0258 if (newStuffEngine.filter === 0) { 0259 return i18nd("knewstuff5", "Everything"); 0260 } else if (newStuffEngine.filter === 1) { 0261 return i18nd("knewstuff5", "Installed"); 0262 } else if (newStuffEngine.filter === 2) { 0263 return i18nd("knewstuff5", "Updateable"); 0264 } else { 0265 // then it's ExactEntryId and we want to probably just ignore that 0266 } 0267 } 0268 checkable: false 0269 icon.name: { 0270 if (newStuffEngine.filter === 0) { 0271 return "package-available" 0272 } else if (newStuffEngine.filter === 1) { 0273 return "package-installed-updated" 0274 } else if (newStuffEngine.filter === 2) { 0275 return "package-installed-outdated" 0276 } else { 0277 // then it's ExactEntryId and we want to probably just ignore that 0278 } 0279 } 0280 Kirigami.Action { 0281 icon.name: "package-available" 0282 text: i18ndc("knewstuff5", "List option which will set the filter to show everything", "Show All Entries") 0283 checkable: true 0284 checked: newStuffEngine.filter === 0 0285 onTriggered: { newStuffEngine.filter = 0; } 0286 QtControls.ActionGroup.group: viewFilterActionGroup 0287 } 0288 Kirigami.Action { 0289 icon.name: "package-installed-updated" 0290 text: i18ndc("knewstuff5", "List option which will set the filter so only installed items are shown", "Show Only Installed Entries") 0291 checkable: true 0292 checked: newStuffEngine.filter === 1 0293 onTriggered: { newStuffEngine.filter = 1; } 0294 QtControls.ActionGroup.group: viewFilterActionGroup 0295 } 0296 Kirigami.Action { 0297 icon.name: "package-installed-outdated" 0298 text: i18ndc("knewstuff5", "List option which will set the filter so only installed items with updates available are shown", "Show Only Updateable Entries") 0299 checkable: true 0300 checked: newStuffEngine.filter === 2 0301 onTriggered: { newStuffEngine.filter = 2; } 0302 QtControls.ActionGroup.group: viewFilterActionGroup 0303 } 0304 }, 0305 Kirigami.Action { 0306 text: { 0307 if (newStuffEngine.sortOrder === 0) { 0308 return i18nd("knewstuff5", "Recent"); 0309 } else if (newStuffEngine.sortOrder === 1) { 0310 return i18nd("knewstuff5", "Alphabetical"); 0311 } else if (newStuffEngine.sortOrder === 2) { 0312 return i18nd("knewstuff5", "Rating"); 0313 } else if (newStuffEngine.sortOrder === 3) { 0314 return i18nd("knewstuff5", "Downloads"); 0315 } else { 0316 } 0317 } 0318 checkable: false 0319 icon.name: { 0320 if (newStuffEngine.sortOrder === 0) { 0321 return "change-date-symbolic"; 0322 } else if (newStuffEngine.sortOrder === 1) { 0323 return "sort-name"; 0324 } else if (newStuffEngine.sortOrder === 2) { 0325 return "rating"; 0326 } else if (newStuffEngine.sortOrder === 3) { 0327 return "download"; 0328 } else { 0329 } 0330 } 0331 Kirigami.Action { 0332 icon.name: "change-date-symbolic" 0333 text: i18ndc("knewstuff5", "List option which will set the sort order to based on when items were most recently updated", "Show Most Recent First") 0334 checkable: true 0335 checked: newStuffEngine.sortOrder === 0 0336 onTriggered: { newStuffEngine.sortOrder = 0; } 0337 QtControls.ActionGroup.group: viewSortingActionGroup 0338 } 0339 Kirigami.Action { 0340 icon.name: "sort-name" 0341 text: i18ndc("knewstuff5", "List option which will set the sort order to be alphabetical based on the name", "Sort Alphabetically By Name") 0342 checkable: true 0343 checked: newStuffEngine.sortOrder === 1 0344 onTriggered: { newStuffEngine.sortOrder = 1; } 0345 QtControls.ActionGroup.group: viewSortingActionGroup 0346 } 0347 Kirigami.Action { 0348 icon.name: "rating" 0349 text: i18ndc("knewstuff5", "List option which will set the sort order to based on user ratings", "Show Highest Rated First") 0350 checkable: true 0351 checked: newStuffEngine.sortOrder === 2 0352 onTriggered: { newStuffEngine.sortOrder = 2; } 0353 QtControls.ActionGroup.group: viewSortingActionGroup 0354 } 0355 Kirigami.Action { 0356 icon.name: "download" 0357 text: i18ndc("knewstuff5", "List option which will set the sort order to based on number of downloads", "Show Most Downloaded First") 0358 checkable: true 0359 checked: newStuffEngine.sortOrder === 3 0360 onTriggered: { newStuffEngine.sortOrder = 3; } 0361 QtControls.ActionGroup.group: viewSortingActionGroup 0362 } 0363 }, 0364 Kirigami.Action { 0365 id: uploadAction 0366 text: i18nd("knewstuff5", "Upload...") 0367 tooltip: i18nd("knewstuff5", "Learn how to add your own hot new stuff to this list") 0368 iconName: "upload-media" 0369 visible: newStuffEngine.engine.uploadEnabled 0370 onTriggered: { 0371 pageStack.push(uploadPage); 0372 } 0373 }, 0374 Kirigami.Action { 0375 text: i18nd("knewstuff5", "Go to...") 0376 iconName: "go-next"; 0377 id: searchModelActions; 0378 visible: children.length > 0; 0379 }, 0380 Kirigami.Action { 0381 text: i18nd("knewstuff5", "Search...") 0382 iconName: "system-search"; 0383 displayHint: Kirigami.DisplayHint.KeepVisible 0384 displayComponent: Kirigami.SearchField { 0385 enabled: engine.isValid 0386 id: searchField 0387 focusSequence: "Ctrl+F" 0388 placeholderText: i18nd("knewstuff5", "Search...") 0389 text: newStuffEngine.searchTerm 0390 onAccepted: { newStuffEngine.searchTerm = searchField.text; } 0391 Component.onCompleted: if (!Kirigami.InputMethod.willShowOnActive) { 0392 forceActiveFocus(); 0393 } 0394 } 0395 } 0396 ] 0397 } 0398 0399 Instantiator { 0400 id: searchPresetInstatiator 0401 model: newStuffEngine.searchPresetModel 0402 Kirigami.Action { 0403 text: model.displayName 0404 iconName: model.iconName 0405 property int indexEntry: index; 0406 onTriggered: { 0407 var curIndex = newStuffEngine.searchPresetModel.index(indexEntry, 0); 0408 newStuffEngine.searchPresetModel.loadSearch(curIndex); 0409 } 0410 } 0411 onObjectAdded: { searchModelActions.children.push(object); } 0412 } 0413 0414 Connections { 0415 target: newStuffEngine.searchPresetModel 0416 function onModelReset() { searchModelActions.children = []; } 0417 } 0418 0419 extraFooterTopPadding: false 0420 footer: QtLayouts.RowLayout { 0421 visible: visibleChildren.length > 0 0422 height: visible ? implicitHeight : 0 0423 0424 QtControls.Label { 0425 visible: categoriesCombo.count > 2 0426 text: i18nd("knewstuff5", "Category:") 0427 } 0428 0429 QtControls.ComboBox { 0430 id: categoriesCombo 0431 QtLayouts.Layout.fillWidth: true 0432 visible: count > 2 0433 model: newStuffEngine.categories 0434 textRole: "displayName" 0435 onCurrentIndexChanged: { 0436 newStuffEngine.categoriesFilter = model.data(model.index(currentIndex, 0), NewStuff.CategoriesModel.NameRole); 0437 } 0438 } 0439 0440 QtControls.Button { 0441 QtLayouts.Layout.alignment: Qt.AlignRight 0442 text: i18nd("knewstuff5", "Contribute your own…") 0443 icon.name: "upload-media" 0444 visible: newStuffEngine.engine.uploadEnabled && !uploadAction.visible 0445 onClicked: { 0446 pageStack.push(uploadPage); 0447 } 0448 } 0449 } 0450 0451 view.model: NewStuff.ItemsModel { 0452 id: newStuffModel; 0453 engine: newStuffEngine; 0454 } 0455 NewStuff.DownloadItemsSheet { 0456 id: downloadItemsSheet 0457 onItemPicked: { 0458 newStuffModel.installItem(entryId, downloadItemId); 0459 } 0460 } 0461 0462 view.implicitCellWidth: root.viewMode == Page.ViewMode.Tiles ? Kirigami.Units.gridUnit * 30 : (root.viewMode == Page.ViewMode.Preview ? Kirigami.Units.gridUnit * 25 : Kirigami.Units.gridUnit * 10) 0463 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) 0464 view.delegate: root.viewMode == Page.ViewMode.Tiles ? tileDelegate : (root.viewMode == Page.ViewMode.Preview ? bigPreviewDelegate : thumbDelegate) 0465 0466 Component { 0467 id: bigPreviewDelegate 0468 EntryGridDelegates.BigPreviewDelegate { } 0469 } 0470 Component { 0471 id: tileDelegate 0472 EntryGridDelegates.TileDelegate { 0473 useLabel: root.useLabel 0474 uninstallLabel: root.uninstallLabel 0475 } 0476 } 0477 Component { 0478 id: thumbDelegate 0479 EntryGridDelegates.ThumbDelegate { 0480 useLabel: root.useLabel 0481 uninstallLabel: root.uninstallLabel 0482 } 0483 } 0484 0485 Component { 0486 id: detailsPage; 0487 NewStuff.EntryDetails { } 0488 } 0489 Component { 0490 id: uploadPage 0491 NewStuff.UploadPage { 0492 engine: newStuffEngine 0493 } 0494 } 0495 0496 Item { 0497 id: loadingOverlay 0498 anchors.fill: parent 0499 opacity: (newStuffEngine.isLoading || newStuffModel.isLoadingData) ? 1 : 0 0500 Behavior on opacity { NumberAnimation { duration: Kirigami.Units.longDuration; } } 0501 visible: opacity > 0 0502 Rectangle { 0503 anchors.fill: parent 0504 color: Kirigami.Theme.backgroundColor 0505 } 0506 Kirigami.LoadingPlaceholder { 0507 anchors.centerIn: parent 0508 text: i18ndc("knewstuff5", "A text shown beside a busy indicator suggesting that data is being fetched", "Loading more…") 0509 } 0510 } 0511 }