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 SPDX-FileCopyrightText: 2023 ivan tkachenko <me@ratijas.tk> 0004 0005 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0006 */ 0007 0008 /** 0009 * @brief A Kirigami.Page component used for managing KNS entries 0010 * 0011 * This component is functionally equivalent to the old DownloadDialog 0012 * @see KNewStuff::DownloadDialog 0013 * @since 5.63 0014 */ 0015 0016 import QtQuick 0017 import QtQuick.Controls as QQC2 0018 import QtQuick.Layouts 0019 0020 import org.kde.kcmutils as KCM 0021 import org.kde.kirigami 2 as Kirigami 0022 import org.kde.newstuff as NewStuff 0023 0024 import "private" as Private 0025 import "private/entrygriddelegates" as EntryGridDelegates 0026 0027 KCM.GridViewKCM { 0028 id: root 0029 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 0038 readonly property alias engine: newStuffEngine 0039 0040 /** 0041 * Whether or not to show the Upload... context action 0042 * Usually this will be bound to the engine's property which usually defines 0043 * this, but you can override it programmatically by setting it here. 0044 * @since 5.85 0045 * @see KNSCore::Engine::uploadEnabled 0046 */ 0047 property alias showUploadAction: uploadAction.visible 0048 0049 /** 0050 * Show the details page for a specific entry. 0051 * If you call this function before the engine initialisation has been completed, 0052 * the action itself will be postponed until that has happened. 0053 * @param providerId The provider ID for the entry you wish to show details for 0054 * @param entryId The unique ID for the entry you wish to show details for 0055 * @since 5.79 0056 */ 0057 function showEntryDetails(providerId, entryId) { 0058 _showEntryDetailsThrottle.enabled = true; 0059 _showEntryDetailsThrottle.entry = newStuffEngine. __createEntry(providerId, entryId); 0060 if (newStuffEngine.busyState === NewStuff.Engine.Initializing) { 0061 _showEntryDetailsThrottle.queryWhenInitialized = true; 0062 } else { 0063 _showEntryDetailsThrottle.requestDetails(); 0064 } 0065 } 0066 0067 // Helper for loading and showing entry details 0068 Connections { 0069 id: _showEntryDetailsThrottle 0070 target: newStuffModel.engine 0071 enabled: false 0072 0073 property var entry 0074 property bool queryWhenInitialized: false 0075 0076 function requestDetails() { 0077 newStuffEngine.updateEntryContents(entry); 0078 queryWhenInitialized = false; 0079 } 0080 0081 function onBusyStateChanged() { 0082 if (queryWhenInitialized && newStuffEngine.busyState !== NewStuff.Engine.Initializing) { 0083 requestDetails(); 0084 queryWhenInitialized = false; 0085 } 0086 } 0087 0088 function onSignalEntryEvent(changedEntry, event) { 0089 if (event === NewStuff.Engine.DetailsLoadedEvent && changedEntry === entry) { // only uniqueId and providerId are checked for equality 0090 enabled = false; 0091 pageStack.push(detailsPage, { 0092 newStuffModel, 0093 providerId: changedEntry.providerId, 0094 entry: changedEntry, 0095 }); 0096 } 0097 } 0098 } 0099 0100 Connections { 0101 id: _restoreSearchState 0102 0103 target: pageStack 0104 enabled: false 0105 0106 function onCurrentIndexChanged() { 0107 if (pageStack.currentIndex === 0) { 0108 newStuffEngine.restoreSearch(); 0109 _restoreSearchState.enabled = false; 0110 } 0111 } 0112 } 0113 0114 property string uninstallLabel: i18ndc("knewstuff6", "Request uninstallation of this item", "Uninstall") 0115 property string useLabel: engine.useLabel 0116 0117 property int viewMode: Page.ViewMode.Tiles 0118 0119 enum ViewMode { 0120 Tiles, 0121 Icons, 0122 Preview 0123 } 0124 0125 // Otherwise the first item will be focused, see BUG: 424894 0126 Component.onCompleted: { 0127 view.currentIndex = -1; 0128 } 0129 0130 title: newStuffEngine.name 0131 0132 view.header: Item { 0133 implicitWidth: view.width - Kirigami.Units.gridUnit 0134 implicitHeight: Kirigami.Units.gridUnit * 3 0135 visible: !loadingOverlay.visible 0136 0137 Kirigami.InlineMessage { 0138 anchors.fill: parent 0139 anchors.margins: Kirigami.Units.smallSpacing 0140 visible: true 0141 text: i18nd("knewstuff6", "The content available here has been uploaded by users like you, and has not been reviewed by your distributor for functionality or stability.") 0142 } 0143 } 0144 0145 NewStuff.Engine { 0146 id: newStuffEngine 0147 } 0148 0149 NewStuff.QuestionAsker {} 0150 Private.ErrorDisplayer { 0151 engine: newStuffEngine 0152 active: root.isCurrentPage 0153 } 0154 0155 QQC2.ActionGroup { id: viewModeActionGroup } 0156 QQC2.ActionGroup { id: viewFilterActionGroup } 0157 QQC2.ActionGroup { id: viewSortingActionGroup } 0158 0159 actions: [ 0160 Kirigami.Action { 0161 visible: newStuffEngine.needsLazyLoadSpinner 0162 displayComponent: QQC2.BusyIndicator { 0163 implicitWidth: Kirigami.Units.iconSizes.smallMedium 0164 implicitHeight: Kirigami.Units.iconSizes.smallMedium 0165 } 0166 }, 0167 0168 Kirigami.Action { 0169 text: { 0170 if (root.viewMode === Page.ViewMode.Tiles) { 0171 return i18nd("knewstuff6", "Tiles"); 0172 } else if (root.viewMode === Page.ViewMode.Icons) { 0173 return i18nd("knewstuff6", "Icons"); 0174 } else { 0175 return i18nd("knewstuff6", "Preview"); 0176 } 0177 } 0178 checkable: false 0179 icon.name: { 0180 if (root.viewMode === Page.ViewMode.Tiles) { 0181 return "view-list-details"; 0182 } else if (root.viewMode === Page.ViewMode.Icons) { 0183 return "view-list-icons"; 0184 } else { 0185 return "view-preview"; 0186 } 0187 } 0188 0189 Kirigami.Action { 0190 icon.name: "view-list-details" 0191 text: i18nd("knewstuff6", "Detailed Tiles View Mode") 0192 onTriggered: { root.viewMode = Page.ViewMode.Tiles; } 0193 checked: root.viewMode === Page.ViewMode.Tiles 0194 checkable: true 0195 QQC2.ActionGroup.group: viewModeActionGroup 0196 } 0197 0198 Kirigami.Action { 0199 icon.name: "view-list-icons" 0200 text: i18nd("knewstuff6", "Icons Only View Mode") 0201 onTriggered: { root.viewMode = Page.ViewMode.Icons; } 0202 checked: root.viewMode === Page.ViewMode.Icons 0203 checkable: true 0204 QQC2.ActionGroup.group: viewModeActionGroup 0205 } 0206 0207 Kirigami.Action { 0208 icon.name: "view-preview" 0209 text: i18nd("knewstuff6", "Large Preview View Mode") 0210 onTriggered: { root.viewMode = Page.ViewMode.Preview; } 0211 checked: root.viewMode === Page.ViewMode.Preview 0212 checkable: true 0213 QQC2.ActionGroup.group: viewModeActionGroup 0214 } 0215 }, 0216 0217 Kirigami.Action { 0218 text: { 0219 if (newStuffEngine.filter === 0) { 0220 return i18nd("knewstuff6", "Everything"); 0221 } else if (newStuffEngine.filter === 1) { 0222 return i18nd("knewstuff6", "Installed"); 0223 } else if (newStuffEngine.filter === 2) { 0224 return i18nd("knewstuff6", "Updateable"); 0225 } else { 0226 // then it's ExactEntryId and we want to probably just ignore that 0227 } 0228 } 0229 checkable: false 0230 icon.name: { 0231 if (newStuffEngine.filter === 0) { 0232 return "package-available" 0233 } else if (newStuffEngine.filter === 1) { 0234 return "package-installed-updated" 0235 } else if (newStuffEngine.filter === 2) { 0236 return "package-installed-outdated" 0237 } else { 0238 // then it's ExactEntryId and we want to probably just ignore that 0239 } 0240 } 0241 0242 Kirigami.Action { 0243 icon.name: "package-available" 0244 text: i18ndc("knewstuff6", "List option which will set the filter to show everything", "Show All Entries") 0245 checkable: true 0246 checked: newStuffEngine.filter === 0 0247 onTriggered: { newStuffEngine.filter = 0; } 0248 QQC2.ActionGroup.group: viewFilterActionGroup 0249 } 0250 0251 Kirigami.Action { 0252 icon.name: "package-installed-updated" 0253 text: i18ndc("knewstuff6", "List option which will set the filter so only installed items are shown", "Show Only Installed Entries") 0254 checkable: true 0255 checked: newStuffEngine.filter === 1 0256 onTriggered: { newStuffEngine.filter = 1; } 0257 QQC2.ActionGroup.group: viewFilterActionGroup 0258 } 0259 0260 Kirigami.Action { 0261 icon.name: "package-installed-outdated" 0262 text: i18ndc("knewstuff6", "List option which will set the filter so only installed items with updates available are shown", "Show Only Updateable Entries") 0263 checkable: true 0264 checked: newStuffEngine.filter === 2 0265 onTriggered: { newStuffEngine.filter = 2; } 0266 QQC2.ActionGroup.group: viewFilterActionGroup 0267 } 0268 }, 0269 0270 Kirigami.Action { 0271 text: { 0272 if (newStuffEngine.sortOrder === 0) { 0273 return i18nd("knewstuff6", "Recent"); 0274 } else if (newStuffEngine.sortOrder === 1) { 0275 return i18nd("knewstuff6", "Alphabetical"); 0276 } else if (newStuffEngine.sortOrder === 2) { 0277 return i18nd("knewstuff6", "Rating"); 0278 } else if (newStuffEngine.sortOrder === 3) { 0279 return i18nd("knewstuff6", "Downloads"); 0280 } else { 0281 } 0282 } 0283 checkable: false 0284 icon.name: { 0285 if (newStuffEngine.sortOrder === 0) { 0286 return "change-date-symbolic"; 0287 } else if (newStuffEngine.sortOrder === 1) { 0288 return "sort-name"; 0289 } else if (newStuffEngine.sortOrder === 2) { 0290 return "rating"; 0291 } else if (newStuffEngine.sortOrder === 3) { 0292 return "download"; 0293 } else { 0294 } 0295 } 0296 0297 Kirigami.Action { 0298 icon.name: "change-date-symbolic" 0299 text: i18ndc("knewstuff6", "List option which will set the sort order to based on when items were most recently updated", "Show Most Recent First") 0300 checkable: true 0301 checked: newStuffEngine.sortOrder === 0 0302 onTriggered: { newStuffEngine.sortOrder = 0; } 0303 QQC2.ActionGroup.group: viewSortingActionGroup 0304 } 0305 0306 Kirigami.Action { 0307 icon.name: "sort-name" 0308 text: i18ndc("knewstuff6", "List option which will set the sort order to be alphabetical based on the name", "Sort Alphabetically By Name") 0309 checkable: true 0310 checked: newStuffEngine.sortOrder === 1 0311 onTriggered: { newStuffEngine.sortOrder = 1; } 0312 QQC2.ActionGroup.group: viewSortingActionGroup 0313 } 0314 0315 Kirigami.Action { 0316 icon.name: "rating" 0317 text: i18ndc("knewstuff6", "List option which will set the sort order to based on user ratings", "Show Highest Rated First") 0318 checkable: true 0319 checked: newStuffEngine.sortOrder === 2 0320 onTriggered: { newStuffEngine.sortOrder = 2; } 0321 QQC2.ActionGroup.group: viewSortingActionGroup 0322 } 0323 0324 Kirigami.Action { 0325 icon.name: "download" 0326 text: i18ndc("knewstuff6", "List option which will set the sort order to based on number of downloads", "Show Most Downloaded First") 0327 checkable: true 0328 checked: newStuffEngine.sortOrder === 3 0329 onTriggered: { newStuffEngine.sortOrder = 3; } 0330 QQC2.ActionGroup.group: viewSortingActionGroup 0331 } 0332 }, 0333 0334 Kirigami.Action { 0335 id: uploadAction 0336 0337 text: i18nd("knewstuff6", "Upload…") 0338 tooltip: i18nd("knewstuff6", "Learn how to add your own hot new stuff to this list") 0339 icon.name: "upload-media" 0340 visible: newStuffEngine.uploadEnabled 0341 0342 onTriggered: { 0343 pageStack.push(uploadPage); 0344 } 0345 }, 0346 0347 Kirigami.Action { 0348 text: i18nd("knewstuff6", "Go to…") 0349 icon.name: "go-next" 0350 id: searchModelActions 0351 visible: children.length > 0 0352 }, 0353 0354 Kirigami.Action { 0355 text: i18nd("knewstuff6", "Search…") 0356 icon.name: "system-search" 0357 displayHint: Kirigami.DisplayHint.KeepVisible 0358 0359 displayComponent: Kirigami.SearchField { 0360 id: searchField 0361 0362 enabled: engine.isValid 0363 focusSequence: "Ctrl+F" 0364 placeholderText: i18nd("knewstuff6", "Search…") 0365 text: newStuffEngine.searchTerm 0366 0367 onAccepted: { 0368 newStuffEngine.searchTerm = searchField.text; 0369 } 0370 0371 Component.onCompleted: { 0372 if (!Kirigami.InputMethod.willShowOnActive) { 0373 forceActiveFocus(); 0374 } 0375 } 0376 } 0377 } 0378 ] 0379 0380 Instantiator { 0381 id: searchPresetInstatiator 0382 0383 model: newStuffEngine.searchPresetModel 0384 0385 Kirigami.Action { 0386 required property int index 0387 0388 text: model.displayName 0389 icon.name: model.iconName 0390 0391 onTriggered: { 0392 const curIndex = newStuffEngine.searchPresetModel.index(index, 0); 0393 newStuffEngine.searchPresetModel.loadSearch(curIndex); 0394 } 0395 } 0396 0397 onObjectAdded: (index, object) => { 0398 searchModelActions.children.push(object); 0399 } 0400 } 0401 0402 Connections { 0403 target: newStuffEngine.searchPresetModel 0404 0405 function onModelReset() { 0406 searchModelActions.children = []; 0407 } 0408 } 0409 0410 footer: RowLayout { 0411 spacing: Kirigami.Units.smallSpacing 0412 0413 visible: visibleChildren.length > 0 0414 height: visible ? implicitHeight : 0 0415 0416 QQC2.Label { 0417 visible: categoriesCombo.count > 2 0418 text: i18nd("knewstuff6", "Category:") 0419 } 0420 0421 QQC2.ComboBox { 0422 id: categoriesCombo 0423 0424 Layout.fillWidth: true 0425 0426 visible: count > 2 0427 model: newStuffEngine.categories 0428 textRole: "displayName" 0429 0430 onCurrentIndexChanged: { 0431 newStuffEngine.categoriesFilter = model.data(model.index(currentIndex, 0), NewStuff.CategoriesModel.NameRole); 0432 } 0433 } 0434 0435 QQC2.Button { 0436 Layout.alignment: Qt.AlignRight 0437 0438 text: i18nd("knewstuff6", "Contribute your own…") 0439 icon.name: "upload-media" 0440 visible: newStuffEngine.uploadEnabled && !uploadAction.visible 0441 0442 onClicked: { 0443 pageStack.push(uploadPage); 0444 } 0445 } 0446 } 0447 0448 view.model: NewStuff.ItemsModel { 0449 id: newStuffModel 0450 0451 engine: newStuffEngine 0452 } 0453 0454 NewStuff.DownloadItemsSheet { 0455 id: downloadItemsSheet 0456 0457 onItemPicked: (entry, downloadItemId) => { 0458 newStuffModel.engine.install(entry, downloadItemId); 0459 } 0460 } 0461 0462 view.implicitCellWidth: switch (root.viewMode) { 0463 case Page.ViewMode.Tiles: 0464 return Kirigami.Units.gridUnit * 30; 0465 0466 case Page.ViewMode.Preview: 0467 return Kirigami.Units.gridUnit * 25; 0468 0469 case Page.ViewMode.Icons: 0470 default: 0471 return Kirigami.Units.gridUnit * 10; 0472 } 0473 0474 view.implicitCellHeight: switch (root.viewMode) { 0475 case Page.ViewMode.Tiles: 0476 return Math.round(view.implicitCellWidth / 3); 0477 0478 case Page.ViewMode.Preview: 0479 return Kirigami.Units.gridUnit * 25; 0480 0481 case Page.ViewMode.Icons: 0482 default: 0483 return Math.round(view.implicitCellWidth / 1.6) + Kirigami.Units.gridUnit * 2; 0484 } 0485 0486 view.delegate: switch (root.viewMode) { 0487 case Page.ViewMode.Tiles: 0488 return tileDelegate; 0489 0490 case Page.ViewMode.Preview: 0491 return bigPreviewDelegate; 0492 0493 case Page.ViewMode.Icons: 0494 default: 0495 return thumbDelegate; 0496 } 0497 0498 Component { 0499 id: bigPreviewDelegate 0500 0501 EntryGridDelegates.BigPreviewDelegate { } 0502 } 0503 0504 Component { 0505 id: tileDelegate 0506 0507 EntryGridDelegates.TileDelegate { 0508 useLabel: root.useLabel 0509 uninstallLabel: root.uninstallLabel 0510 } 0511 } 0512 0513 Component { 0514 id: thumbDelegate 0515 0516 EntryGridDelegates.ThumbDelegate { 0517 useLabel: root.useLabel 0518 uninstallLabel: root.uninstallLabel 0519 } 0520 } 0521 0522 Component { 0523 id: detailsPage 0524 0525 NewStuff.EntryDetails { } 0526 } 0527 0528 Component { 0529 id: uploadPage 0530 0531 NewStuff.UploadPage { 0532 engine: newStuffEngine 0533 } 0534 } 0535 0536 Item { 0537 id: loadingOverlay 0538 0539 anchors.fill: parent 0540 0541 opacity: newStuffEngine.isLoading && !newStuffEngine.needsLazyLoadSpinner ? 1 : 0 0542 Behavior on opacity { 0543 NumberAnimation { 0544 duration: Kirigami.Units.longDuration 0545 } 0546 } 0547 0548 visible: opacity > 0 0549 0550 Rectangle { 0551 anchors.fill: parent 0552 color: Kirigami.Theme.backgroundColor 0553 } 0554 0555 Kirigami.LoadingPlaceholder { 0556 anchors.centerIn: parent 0557 text: newStuffEngine.busyMessage 0558 } 0559 } 0560 }