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 ? xi18ndc("knewstuff6", "@info displayed as InlineMessage", "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 : i18ndc("knewstuff6", "@info displayed as InlineMessage", "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 i18ndc("knewstuff6", "@action:button opening menu similar to combobox, filter list", "All");
173 } else if (newStuffEngine.filter === 1) {
174 return i18ndc("knewstuff6", "@action:button opening menu similar to combobox, filter list", "Installed");
175 } else if (newStuffEngine.filter === 2) {
176 return i18ndc("knewstuff6", "@action:button opening menu similar to combobox, filter list", "Updateable");
177 } else {
178 // then it's ExactEntryId and we want to probably just ignore that
179 }
180 }
181 checkable: false
182 icon.name: "view-filter"
183
184 Kirigami.Action {
185 icon.name: "package-available"
186 text: i18ndc("knewstuff6", "@option:radio similar to combobox item, List option which will set the filter to show everything", "All")
187 checkable: true
188 checked: newStuffEngine.filter === 0
189 onTriggered: source => {
190 newStuffEngine.filter = 0;
191 }
192 QQC2.ActionGroup.group: viewFilterActionGroup
193 }
194
195 Kirigami.Action {
196 icon.name: "package-installed-updated"
197 text: i18ndc("knewstuff6", "@option:radio similar to combobox item, List option which will set the filter so only installed items are shown", "Installed")
198 checkable: true
199 checked: newStuffEngine.filter === 1
200 onTriggered: source => {
201 newStuffEngine.filter = 1;
202 }
203 QQC2.ActionGroup.group: viewFilterActionGroup
204 }
205
206 Kirigami.Action {
207 icon.name: "package-installed-outdated"
208 text: i18ndc("knewstuff6", "@option:radio similar to combobox item, List option which will set the filter so only installed items with updates available are shown", "Updateable")
209 checkable: true
210 checked: newStuffEngine.filter === 2
211 onTriggered: source => {
212 newStuffEngine.filter = 2;
213 }
214 QQC2.ActionGroup.group: viewFilterActionGroup
215 }
216 },
217
218 Kirigami.Action {
219 text: {
220 if (newStuffEngine.sortOrder === 0) {
221 return i18ndc("knewstuff6", "@action:button opening menu similar to combobox, filter list", "Sort: Release date");
222 } else if (newStuffEngine.sortOrder === 1) {
223 return i18ndc("knewstuff6", "@action:button opening menu similar to combobox, filter list", "Sort: Name");
224 } else if (newStuffEngine.sortOrder === 2) {
225 return i18ndc("knewstuff6", "@action:button opening menu similar to combobox, filter list", "Sort: Rating");
226 } else if (newStuffEngine.sortOrder === 3) {
227 return i18ndc("knewstuff6", "@action:button opening menu similar to combobox, filter list", "Sort: Downloads");
228 } else {
229 }
230 }
231 checkable: false
232 icon.name: "view-sort"
233
234 Kirigami.Action {
235 icon.name: "sort-name"
236 text: i18ndc("knewstuff6", "@option:radio in menu, List option which will set the sort order to be alphabetical based on the name", "Name")
237 checkable: true
238 checked: newStuffEngine.sortOrder === 1
239 onTriggered: source => {
240 newStuffEngine.sortOrder = 1;
241 }
242 QQC2.ActionGroup.group: viewSortingActionGroup
243 }
244
245 Kirigami.Action {
246 icon.name: "rating"
247 text: i18ndc("knewstuff6", "@option:radio in menu, List option which will set the sort order to based on user ratings", "Rating")
248 checkable: true
249 checked: newStuffEngine.sortOrder === 2
250 onTriggered: source => {
251 newStuffEngine.sortOrder = 2;
252 }
253 QQC2.ActionGroup.group: viewSortingActionGroup
254 }
255
256 Kirigami.Action {
257 icon.name: "download"
258 text: i18ndc("knewstuff6", "@option:radio similar to combobox item, List option which will set the sort order to based on number of downloads", "Downloads")
259 checkable: true
260 checked: newStuffEngine.sortOrder === 3
261 onTriggered: source => {
262 newStuffEngine.sortOrder = 3;
263 }
264 QQC2.ActionGroup.group: viewSortingActionGroup
265 }
266
267 Kirigami.Action {
268 icon.name: "change-date-symbolic"
269 text: i18ndc("knewstuff6", "@option:radio similar to combobox item, List option which will set the sort order to based on when items were most recently updated", "Release date")
270 checkable: true
271 checked: newStuffEngine.sortOrder === 0
272 onTriggered: source => {
273 newStuffEngine.sortOrder = 0;
274 }
275 QQC2.ActionGroup.group: viewSortingActionGroup
276 }
277 },
278
279 Kirigami.Action {
280 id: uploadAction
281
282 text: i18nd("knewstuff6", "Upload…")
283 tooltip: i18nd("knewstuff6", "Learn how to add your own hot new stuff to this list")
284 icon.name: "upload-media"
285 visible: newStuffEngine.uploadEnabled
286
287 onTriggered: source => {
288 pageStack.push(uploadPage);
289 }
290 },
291
292 Kirigami.Action {
293 text: i18nd("knewstuff6", "Go to…")
294 icon.name: "go-next"
295 id: searchModelActions
296 visible: children.length > 0
297 },
298
300 text: i18nd("knewstuff6", "Search…")
301 icon.name: "system-search"
302 displayHint: Kirigami.DisplayHint.KeepVisible
303
304 displayComponent: Kirigami.SearchField {
305 id: searchField
306
307 enabled: engine.isValid
308 focusSequence: "Ctrl+F"
309 placeholderText: i18nd("knewstuff6", "Search…")
310 text: newStuffEngine.searchTerm
311
312 onAccepted: {
313 newStuffEngine.searchTerm = searchField.text;
314 }
315
316 Component.onCompleted: {
317 if (!Kirigami.InputMethod.willShowOnActive) {
318 forceActiveFocus();
319 }
320 }
321 }
322 }
323 ]
324
325 Instantiator {
326 id: searchPresetInstatiator
327
328 model: newStuffEngine.searchPresetModel
329
330 Kirigami.Action {
331 required property int index
332
333 text: model.displayName
334 icon.name: model.iconName
335
336 onTriggered: source => {
337 const curIndex = newStuffEngine.searchPresetModel.index(index, 0);
338 newStuffEngine.searchPresetModel.loadSearch(curIndex);
339 }
340 }
341
342 onObjectAdded: (index, object) => {
343 searchModelActions.children.push(object);
344 }
345 }
346
347 Connections {
348 target: newStuffEngine.searchPresetModel
349
350 function onModelReset() {
351 searchModelActions.children = [];
352 }
353 }
354
355 footer: RowLayout {
356 spacing: Kirigami.Units.smallSpacing
357
358 visible: visibleChildren.length > 0
359 height: visible ? implicitHeight : 0
360
361 QQC2.Label {
362 visible: categoriesCombo.count > 2
363 text: i18nd("knewstuff6", "Category:")
364 }
365
366 QQC2.ComboBox {
367 id: categoriesCombo
368
369 Layout.fillWidth: true
370
371 visible: count > 2
372 model: newStuffEngine.categories
373 textRole: "displayName"
374
375 onCurrentIndexChanged: {
376 newStuffEngine.categoriesFilter = model.data(model.index(currentIndex, 0), NewStuff.CategoriesModel.NameRole);
377 }
378 }
379
380 QQC2.Button {
381 Layout.alignment: Qt.AlignRight
382
383 text: i18nd("knewstuff6", "Contribute Your Own…")
384 icon.name: "upload-media"
385 visible: newStuffEngine.uploadEnabled && !uploadAction.visible
386
387 onClicked: {
388 pageStack.push(uploadPage);
389 }
390 }
391 }
392
393 view.model: NewStuff.ItemsModel {
394 id: newStuffModel
395
396 engine: newStuffEngine
397 }
398
399 NewStuff.DownloadItemsSheet {
400 id: downloadItemsSheet
401
402 parent: root.QQC2.Overlay.overlay
403
404 onItemPicked: (entry, downloadItemId) => {
405 newStuffModel.engine.installLinkId(entry, downloadItemId);
406 }
407 }
408
409 view.implicitCellWidth: switch (root.viewMode) {
410 case Page.ViewMode.Preview:
411 return Kirigami.Units.gridUnit * 25;
412
413 case Page.ViewMode.Tiles:
414 case Page.ViewMode.Icons:
415 default:
416 return Kirigami.Units.gridUnit * 30;
417 }
418
419 view.implicitCellHeight: switch (root.viewMode) {
420 case Page.ViewMode.Preview:
421 return Kirigami.Units.gridUnit * 25;
422
423 case Page.ViewMode.Tiles:
424 case Page.ViewMode.Icons:
425 default:
426 return Math.round(view.implicitCellWidth / 3);
427 }
428
429 view.delegate: switch (root.viewMode) {
430 case Page.ViewMode.Preview:
431 return bigPreviewDelegate;
432
433 case Page.ViewMode.Tiles:
434 case Page.ViewMode.Icons:
435 default:
436 return tileDelegate;
437 }
438
439 Component {
440 id: bigPreviewDelegate
441
442 EntryGridDelegates.BigPreviewDelegate { }
443 }
444
445 Component {
446 id: tileDelegate
447
448 EntryGridDelegates.TileDelegate {
449 useLabel: root.useLabel
450 uninstallLabel: root.uninstallLabel
451 }
452 }
453
454 Component {
455 id: detailsPage
456
457 NewStuff.EntryDetails { }
458 }
459
460 Component {
461 id: uploadPage
462
463 NewStuff.UploadPage {
464 engine: newStuffEngine
465 }
466 }
467
468 Item {
469 id: loadingOverlay
470
471 anchors.fill: parent
472
473 opacity: newStuffEngine.isLoading && !newStuffEngine.needsLazyLoadSpinner ? 1 : 0
474 Behavior on opacity {
475 NumberAnimation {
476 duration: Kirigami.Units.longDuration
477 }
478 }
479
480 visible: opacity > 0
481
482 Rectangle {
483 anchors.fill: parent
484 color: Kirigami.Theme.backgroundColor
485 }
486
487 Kirigami.LoadingPlaceholder {
488 anchors.centerIn: parent
489 text: newStuffEngine.busyMessage
490 }
491 }
492}
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 i18nd(const char *domain, const char *text, const TYPE &arg...)
QString xi18ndc(const char *domain, const char *context, const char *text, const TYPE &arg...)
Type type(const QSqlDatabase &db)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:20:03 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.