KNewStuff

Page.qml
1/*
2 SPDX-FileCopyrightText: 2019 Dan Leinir Turthra Jensen <admin@leinir.dk>
3 SPDX-FileCopyrightText: 2023 ivan tkachenko <me@ratijas.tk>
4
5 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
6*/
7
8/**
9 * @brief A Kirigami.Page component used for managing KNS entries
10 *
11 * This component is functionally equivalent to the old DownloadDialog
12 * @see KNewStuff::DownloadDialog
13 * @since 5.63
14 */
15
16import QtQuick
17import QtQuick.Controls as QQC2
18import QtQuick.Layouts
19
20import org.kde.kcmutils as KCMUtils
21import org.kde.kirigami as Kirigami
22import org.kde.newstuff as NewStuff
23
24import "private" as Private
25import "private/entrygriddelegates" as EntryGridDelegates
26
27KCMUtils.GridViewKCM {
28 id: root
29
30 /**
31 * @brief The configuration file which describes the application (knsrc)
32 *
33 * The format and location of this file is found in the documentation for
34 * KNS3::DownloadDialog
35 */
36 property alias configFile: newStuffEngine.configFile
37
38 readonly property alias engine: newStuffEngine
39
40 /**
41 * Whether or not to show the Upload... context action
42 * Usually this will be bound to the engine's property which usually defines
43 * this, but you can override it programmatically by setting it here.
44 * @since 5.85
45 * @see KNSCore::Engine::uploadEnabled
46 */
47 property alias showUploadAction: uploadAction.visible
48
49 /**
50 * Show the details page for a specific entry.
51 * If you call this function before the engine initialisation has been completed,
52 * the action itself will be postponed until that has happened.
53 * @param providerId The provider ID for the entry you wish to show details for
54 * @param entryId The unique ID for the entry you wish to show details for
55 * @since 5.79
56 */
57 function showEntryDetails(providerId, entryId) {
58 _showEntryDetailsThrottle.enabled = true;
59 _showEntryDetailsThrottle.entry = newStuffEngine. __createEntry(providerId, entryId);
60 if (newStuffEngine.busyState === NewStuff.Engine.Initializing) {
61 _showEntryDetailsThrottle.queryWhenInitialized = true;
62 } else {
63 _showEntryDetailsThrottle.requestDetails();
64 }
65 }
66
67 // Helper for loading and showing entry details
68 Connections {
69 id: _showEntryDetailsThrottle
70 target: newStuffModel.engine
71 enabled: false
72
73 property var entry
74 property bool queryWhenInitialized: false
75
76 function requestDetails() {
77 newStuffEngine.updateEntryContents(entry);
78 queryWhenInitialized = false;
79 }
80
81 function onBusyStateChanged() {
82 if (queryWhenInitialized && newStuffEngine.busyState !== NewStuff.Engine.Initializing) {
83 requestDetails();
84 queryWhenInitialized = false;
85 }
86 }
87
88 function onSignalEntryEvent(changedEntry, event) {
89 if (event === NewStuff.Engine.DetailsLoadedEvent && changedEntry === entry) { // only uniqueId and providerId are checked for equality
90 enabled = false;
91 pageStack.push(detailsPage, {
92 newStuffModel,
93 providerId: changedEntry.providerId,
94 entry: changedEntry,
95 });
96 }
97 }
98 }
99
100 Connections {
101 id: _restoreSearchState
102
103 target: pageStack
104 enabled: false
105
106 function onCurrentIndexChanged() {
107 if (pageStack.currentIndex === 0) {
108 newStuffEngine.restoreSearch();
109 _restoreSearchState.enabled = false;
110 }
111 }
112 }
113
114 property string uninstallLabel: i18ndc("knewstuff6", "Request uninstallation of this item", "Uninstall")
115 property string useLabel: engine.useLabel
116
117 property int viewMode: Page.ViewMode.Tiles
118
119 // TODO KF7: remove Icons
120 enum ViewMode {
121 Tiles,
122 Icons,
123 Preview
124 }
125
126 // Otherwise the first item will be focused, see BUG: 424894
127 Component.onCompleted: {
128 view.currentIndex = -1;
129 }
130
131 title: newStuffEngine.name
132
133 headerPaddingEnabled: false
134 header: Kirigami.InlineMessage {
135 readonly property bool riskyContent: newStuffEngine.contentWarningType === NewStuff.Engine.Executables
136 visible: !loadingOverlay.visible
137 type: riskyContent ? Kirigami.MessageType.Warning : Kirigami.MessageType.Information
138 position: Kirigami.InlineMessage.Position.Header
139 text: riskyContent
140 ? xi18nd("knewstuff6", "Use caution when accessing user-created content shown here, as it may contain executable code that hasn't been tested by KDE or your distributor for safety, stability, or quality.")
141 : i18nd("knewstuff6", "User-created content shown here hasn't been tested by KDE or your distributor for functionality or quality.")
142 }
143
144 NewStuff.Engine {
145 id: newStuffEngine
146 }
147
148 NewStuff.QuestionAsker {
149 parent: root.QQC2.Overlay.overlay
150 }
151
152 Private.ErrorDisplayer {
153 engine: newStuffEngine
154 active: root.isCurrentPage
155 }
156
157 QQC2.ActionGroup { id: viewFilterActionGroup }
158 QQC2.ActionGroup { id: viewSortingActionGroup }
159
160 actions: [
162 visible: newStuffEngine.needsLazyLoadSpinner
163 displayComponent: QQC2.BusyIndicator {
164 implicitWidth: Kirigami.Units.iconSizes.smallMedium
165 implicitHeight: Kirigami.Units.iconSizes.smallMedium
166 }
167 },
168
170 text: {
171 if (newStuffEngine.filter === 0) {
172 return i18nd("knewstuff6", "Everything");
173 } else if (newStuffEngine.filter === 1) {
174 return i18nd("knewstuff6", "Installed");
175 } else if (newStuffEngine.filter === 2) {
176 return i18nd("knewstuff6", "Updateable");
177 } else {
178 // then it's ExactEntryId and we want to probably just ignore that
179 }
180 }
181 checkable: false
182 icon.name: {
183 if (newStuffEngine.filter === 0) {
184 return "package-available"
185 } else if (newStuffEngine.filter === 1) {
186 return "package-installed-updated"
187 } else if (newStuffEngine.filter === 2) {
188 return "package-installed-outdated"
189 } else {
190 // then it's ExactEntryId and we want to probably just ignore that
191 }
192 }
193
194 Kirigami.Action {
195 icon.name: "package-available"
196 text: i18ndc("knewstuff6", "List option which will set the filter to show everything", "Show All Entries")
197 checkable: true
198 checked: newStuffEngine.filter === 0
199 onTriggered: source => {
200 newStuffEngine.filter = 0;
201 }
202 QQC2.ActionGroup.group: viewFilterActionGroup
203 }
204
205 Kirigami.Action {
206 icon.name: "package-installed-updated"
207 text: i18ndc("knewstuff6", "List option which will set the filter so only installed items are shown", "Show Only Installed Entries")
208 checkable: true
209 checked: newStuffEngine.filter === 1
210 onTriggered: source => {
211 newStuffEngine.filter = 1;
212 }
213 QQC2.ActionGroup.group: viewFilterActionGroup
214 }
215
216 Kirigami.Action {
217 icon.name: "package-installed-outdated"
218 text: i18ndc("knewstuff6", "List option which will set the filter so only installed items with updates available are shown", "Show Only Updateable Entries")
219 checkable: true
220 checked: newStuffEngine.filter === 2
221 onTriggered: source => {
222 newStuffEngine.filter = 2;
223 }
224 QQC2.ActionGroup.group: viewFilterActionGroup
225 }
226 },
227
228 Kirigami.Action {
229 text: {
230 if (newStuffEngine.sortOrder === 0) {
231 return i18nd("knewstuff6", "Recent");
232 } else if (newStuffEngine.sortOrder === 1) {
233 return i18nd("knewstuff6", "Alphabetical");
234 } else if (newStuffEngine.sortOrder === 2) {
235 return i18nd("knewstuff6", "Rating");
236 } else if (newStuffEngine.sortOrder === 3) {
237 return i18nd("knewstuff6", "Downloads");
238 } else {
239 }
240 }
241 checkable: false
242 icon.name: {
243 if (newStuffEngine.sortOrder === 0) {
244 return "change-date-symbolic";
245 } else if (newStuffEngine.sortOrder === 1) {
246 return "sort-name";
247 } else if (newStuffEngine.sortOrder === 2) {
248 return "rating";
249 } else if (newStuffEngine.sortOrder === 3) {
250 return "download";
251 } else {
252 }
253 }
254
255 Kirigami.Action {
256 icon.name: "change-date-symbolic"
257 text: i18ndc("knewstuff6", "List option which will set the sort order to based on when items were most recently updated", "Show Most Recent First")
258 checkable: true
259 checked: newStuffEngine.sortOrder === 0
260 onTriggered: source => {
261 newStuffEngine.sortOrder = 0;
262 }
263 QQC2.ActionGroup.group: viewSortingActionGroup
264 }
265
266 Kirigami.Action {
267 icon.name: "sort-name"
268 text: i18ndc("knewstuff6", "List option which will set the sort order to be alphabetical based on the name", "Sort Alphabetically By Name")
269 checkable: true
270 checked: newStuffEngine.sortOrder === 1
271 onTriggered: source => {
272 newStuffEngine.sortOrder = 1;
273 }
274 QQC2.ActionGroup.group: viewSortingActionGroup
275 }
276
277 Kirigami.Action {
278 icon.name: "rating"
279 text: i18ndc("knewstuff6", "List option which will set the sort order to based on user ratings", "Show Highest Rated First")
280 checkable: true
281 checked: newStuffEngine.sortOrder === 2
282 onTriggered: source => {
283 newStuffEngine.sortOrder = 2;
284 }
285 QQC2.ActionGroup.group: viewSortingActionGroup
286 }
287
288 Kirigami.Action {
289 icon.name: "download"
290 text: i18ndc("knewstuff6", "List option which will set the sort order to based on number of downloads", "Show Most Downloaded First")
291 checkable: true
292 checked: newStuffEngine.sortOrder === 3
293 onTriggered: source => {
294 newStuffEngine.sortOrder = 3;
295 }
296 QQC2.ActionGroup.group: viewSortingActionGroup
297 }
298 },
299
300 Kirigami.Action {
301 id: uploadAction
302
303 text: i18nd("knewstuff6", "Upload…")
304 tooltip: i18nd("knewstuff6", "Learn how to add your own hot new stuff to this list")
305 icon.name: "upload-media"
306 visible: newStuffEngine.uploadEnabled
307
308 onTriggered: source => {
309 pageStack.push(uploadPage);
310 }
311 },
312
313 Kirigami.Action {
314 text: i18nd("knewstuff6", "Go to…")
315 icon.name: "go-next"
316 id: searchModelActions
317 visible: children.length > 0
318 },
319
321 text: i18nd("knewstuff6", "Search…")
322 icon.name: "system-search"
323 displayHint: Kirigami.DisplayHint.KeepVisible
324
325 displayComponent: Kirigami.SearchField {
326 id: searchField
327
328 enabled: engine.isValid
329 focusSequence: "Ctrl+F"
330 placeholderText: i18nd("knewstuff6", "Search…")
331 text: newStuffEngine.searchTerm
332
333 onAccepted: {
334 newStuffEngine.searchTerm = searchField.text;
335 }
336
337 Component.onCompleted: {
338 if (!Kirigami.InputMethod.willShowOnActive) {
339 forceActiveFocus();
340 }
341 }
342 }
343 }
344 ]
345
346 Instantiator {
347 id: searchPresetInstatiator
348
349 model: newStuffEngine.searchPresetModel
350
351 Kirigami.Action {
352 required property int index
353
354 text: model.displayName
355 icon.name: model.iconName
356
357 onTriggered: source => {
358 const curIndex = newStuffEngine.searchPresetModel.index(index, 0);
359 newStuffEngine.searchPresetModel.loadSearch(curIndex);
360 }
361 }
362
363 onObjectAdded: (index, object) => {
364 searchModelActions.children.push(object);
365 }
366 }
367
368 Connections {
369 target: newStuffEngine.searchPresetModel
370
371 function onModelReset() {
372 searchModelActions.children = [];
373 }
374 }
375
376 footer: RowLayout {
377 spacing: Kirigami.Units.smallSpacing
378
379 visible: visibleChildren.length > 0
380 height: visible ? implicitHeight : 0
381
382 QQC2.Label {
383 visible: categoriesCombo.count > 2
384 text: i18nd("knewstuff6", "Category:")
385 }
386
387 QQC2.ComboBox {
388 id: categoriesCombo
389
390 Layout.fillWidth: true
391
392 visible: count > 2
393 model: newStuffEngine.categories
394 textRole: "displayName"
395
396 onCurrentIndexChanged: {
397 newStuffEngine.categoriesFilter = model.data(model.index(currentIndex, 0), NewStuff.CategoriesModel.NameRole);
398 }
399 }
400
401 QQC2.Button {
402 Layout.alignment: Qt.AlignRight
403
404 text: i18nd("knewstuff6", "Contribute your own…")
405 icon.name: "upload-media"
406 visible: newStuffEngine.uploadEnabled && !uploadAction.visible
407
408 onClicked: {
409 pageStack.push(uploadPage);
410 }
411 }
412 }
413
414 view.model: NewStuff.ItemsModel {
415 id: newStuffModel
416
417 engine: newStuffEngine
418 }
419
420 NewStuff.DownloadItemsSheet {
421 id: downloadItemsSheet
422
423 parent: root.QQC2.Overlay.overlay
424
425 onItemPicked: (entry, downloadItemId) => {
426 newStuffModel.engine.install(entry, downloadItemId);
427 }
428 }
429
430 view.implicitCellWidth: switch (root.viewMode) {
431 case Page.ViewMode.Preview:
432 return Kirigami.Units.gridUnit * 25;
433
434 case Page.ViewMode.Tiles:
435 case Page.ViewMode.Icons:
436 default:
437 return Kirigami.Units.gridUnit * 30;
438 }
439
440 view.implicitCellHeight: switch (root.viewMode) {
441 case Page.ViewMode.Preview:
442 return Kirigami.Units.gridUnit * 25;
443
444 case Page.ViewMode.Tiles:
445 case Page.ViewMode.Icons:
446 default:
447 return Math.round(view.implicitCellWidth / 3);
448 }
449
450 view.delegate: switch (root.viewMode) {
451 case Page.ViewMode.Preview:
452 return bigPreviewDelegate;
453
454 case Page.ViewMode.Tiles:
455 case Page.ViewMode.Icons:
456 default:
457 return tileDelegate;
458 }
459
460 Component {
461 id: bigPreviewDelegate
462
463 EntryGridDelegates.BigPreviewDelegate { }
464 }
465
466 Component {
467 id: tileDelegate
468
469 EntryGridDelegates.TileDelegate {
470 useLabel: root.useLabel
471 uninstallLabel: root.uninstallLabel
472 }
473 }
474
475 Component {
476 id: detailsPage
477
478 NewStuff.EntryDetails { }
479 }
480
481 Component {
482 id: uploadPage
483
484 NewStuff.UploadPage {
485 engine: newStuffEngine
486 }
487 }
488
489 Item {
490 id: loadingOverlay
491
492 anchors.fill: parent
493
494 opacity: newStuffEngine.isLoading && !newStuffEngine.needsLazyLoadSpinner ? 1 : 0
495 Behavior on opacity {
496 NumberAnimation {
497 duration: Kirigami.Units.longDuration
498 }
499 }
500
501 visible: opacity > 0
502
503 Rectangle {
504 anchors.fill: parent
505 color: Kirigami.Theme.backgroundColor
506 }
507
508 Kirigami.LoadingPlaceholder {
509 anchors.centerIn: parent
510 text: newStuffEngine.busyMessage
511 }
512 }
513}
KNSCore::EngineBase for interfacing with QML.
Definition quickengine.h:29
A component used to forward questions from KNewStuff's engine to the UI.
QString i18ndc(const char *domain, const char *context, const char *text, const TYPE &arg...)
QString xi18nd(const char *domain, const char *text, const TYPE &arg...)
QString i18nd(const char *domain, const char *text, const TYPE &arg...)
VehicleSection::Type type(QStringView coachNumber, QStringView coachClassification)
QChar * data()
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jul 26 2024 11:56:35 by doxygen 1.11.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.