Warning, /plasma/discover/discover/qml/ApplicationPage.qml is written in an unsupported language. File is not indexed.
0001 /* 0002 * SPDX-FileCopyrightText: 2012 Aleix Pol Gonzalez <aleixpol@blue-systems.com> 0003 * SPDX-FileCopyrightText: 2022 Nate Graham <nate@kde.org> 0004 * 0005 * SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 import QtQuick 2.15 0009 import QtQuick.Controls 2.15 0010 import QtQuick.Window 2.15 0011 import QtQuick.Layouts 1.15 0012 import org.kde.discover 2.0 0013 import org.kde.discover.app 1.0 0014 import org.kde.kirigami 2.20 as Kirigami 0015 import org.kde.purpose 1.0 as Purpose 0016 import "navigation.js" as Navigation 0017 0018 DiscoverPage { 0019 id: appInfo 0020 0021 title: appInfo.application.name 0022 clip: true 0023 0024 property QtObject application: null 0025 readonly property int visibleReviews: 3 0026 readonly property int internalSpacings: Kirigami.Units.largeSpacing 0027 readonly property int pageContentMargins: Kirigami.Units.gridUnit 0028 0029 // Usually this page is not the top level page, but when we are, isHome being 0030 // true will ensure that the search field suggests we are searching in the list 0031 // of available apps, not inside the app page itself. This will happen when 0032 // Discover is launched e.g. from krunner or otherwise requested to show a 0033 // specific application on launch. 0034 readonly property bool isHome: true 0035 0036 readonly property bool isOfflineUpgrade: application.packageName === "discover-offline-upgrade" 0037 0038 function searchFor(text) { 0039 if (text.length === 0) 0040 return; 0041 Navigation.openCategory(null, "") 0042 } 0043 0044 ReviewsPage { 0045 id: reviewsSheet 0046 model: ReviewsModel { 0047 id: reviewsModel 0048 resource: appInfo.application 0049 } 0050 } 0051 0052 contextualActions: [originsMenuAction] 0053 0054 ActionGroup { 0055 id: sourcesGroup 0056 exclusive: true 0057 } 0058 0059 Kirigami.Action { 0060 id: originsMenuAction 0061 0062 text: i18n("Sources") 0063 visible: children.length>1 0064 children: sourcesGroup.actions 0065 readonly property var r0: Instantiator { 0066 model: ResourcesProxyModel { 0067 id: alternativeResourcesModel 0068 allBackends: true 0069 resourcesUrl: appInfo.application.url 0070 } 0071 delegate: Action { 0072 ActionGroup.group: sourcesGroup 0073 text: model.application.availableVersion ? i18n("%1 - %2", displayOrigin, model.application.availableVersion) : displayOrigin 0074 icon.name: sourceIcon 0075 checkable: true 0076 checked: appInfo.application === model.application 0077 onTriggered: if(index>=0) { 0078 appInfo.application = model.application 0079 } 0080 } 0081 } 0082 } 0083 0084 Kirigami.Action { 0085 id: invokeAction 0086 visible: application.isInstalled && application.canExecute && !appbutton.isActive 0087 text: application.executeLabel 0088 icon.name: "media-playback-start" 0089 onTriggered: application.invokeApplication() 0090 } 0091 0092 actions { 0093 main: appbutton.isActive ? appbutton.cancelAction : appbutton.action 0094 right: invokeAction 0095 } 0096 0097 InstallApplicationButton { 0098 id: appbutton 0099 Layout.rightMargin: Kirigami.Units.smallSpacing 0100 application: appInfo.application 0101 visible: false 0102 } 0103 0104 topPadding: 0 0105 bottomPadding: 0 0106 leftPadding: 0 0107 rightPadding: 0 0108 0109 // Page content 0110 ColumnLayout { 0111 id: pageLayout 0112 0113 spacing: appInfo.internalSpacings 0114 0115 // Header and its background rectangle 0116 Rectangle { 0117 Layout.fillWidth: true 0118 0119 implicitHeight: headerLayout.height + (headerLayout.anchors.topMargin * 2) 0120 color: Qt.darker(Kirigami.Theme.backgroundColor, 1.05) 0121 0122 // Header layout with App icon, name, author, rating, 0123 // screenshots, and metadata 0124 ColumnLayout { 0125 id: headerLayout 0126 0127 anchors { 0128 topMargin: appInfo.internalSpacings 0129 top: parent.top 0130 left: parent.left 0131 right: parent.right 0132 } 0133 0134 spacing: appInfo.internalSpacings 0135 0136 // App icon, name, author or update info, rating 0137 RowLayout { 0138 Layout.leftMargin: appInfo.internalSpacings 0139 Layout.rightMargin: appInfo.internalSpacings 0140 0141 spacing: 0 // Children bring their own 0142 0143 // App icon 0144 Kirigami.Icon { 0145 readonly property int size : applicationWindow().wideScreen ? Kirigami.Units.iconSizes.large * 2 : Kirigami.Units.iconSizes.huge 0146 0147 implicitWidth: size 0148 implicitHeight: size 0149 source: appInfo.application.icon 0150 Layout.rightMargin: appInfo.internalSpacings 0151 } 0152 0153 // App name, author, and rating 0154 ColumnLayout { 0155 Layout.fillWidth: true 0156 0157 spacing: 0 0158 0159 // App name 0160 Kirigami.Heading { 0161 Layout.fillWidth: true 0162 text: appInfo.application.name 0163 type: Kirigami.Heading.Type.Primary 0164 wrapMode: Text.Wrap 0165 maximumLineCount: 5 0166 elide: Text.ElideRight 0167 } 0168 0169 // Author (for apps) or upgrade info (for offline upgrades) 0170 Label { 0171 id: author 0172 0173 Layout.fillWidth: true 0174 Layout.bottomMargin: appInfo.internalSpacings 0175 0176 visible: text.length > 0 0177 0178 opacity: 0.8 0179 text: { 0180 if (appInfo.isOfflineUpgrade) { 0181 return appInfo.application.upgradeText.length > 0 ? appInfo.application.upgradeText : ""; 0182 } else if (appInfo.application.author.length > 0) { 0183 return appInfo.application.author; 0184 } else { 0185 return i18n("Unknown author"); 0186 } 0187 } 0188 wrapMode: Text.Wrap 0189 maximumLineCount: 5 0190 elide: Text.ElideRight 0191 } 0192 0193 // Rating 0194 RowLayout { 0195 Layout.fillWidth: true 0196 0197 // Not relevant to the offline upgrade use case 0198 visible: !appInfo.isOfflineUpgrade 0199 0200 Rating { 0201 opacity: 0.8 0202 rating: appInfo.application.rating ? appInfo.application.rating.sortableRating : 0 0203 starSize: author.font.pointSize 0204 } 0205 0206 Label { 0207 opacity: 0.8 0208 text: appInfo.application.rating ? i18np("%1 rating", "%1 ratings", appInfo.application.rating.ratingCount) : i18n("No ratings yet") 0209 } 0210 } 0211 } 0212 } 0213 0214 // Screenshots 0215 Kirigami.InlineMessage { 0216 type: Kirigami.MessageType.Warning 0217 Layout.fillWidth: true 0218 Layout.margins: Kirigami.Units.smallSpacing 0219 visible: screenshots.hasFailed 0220 text: i18n("Could not access the screenshots") 0221 } 0222 0223 ScrollView { 0224 id: screenshotsScroll 0225 visible: screenshots.count > 0 && !screenshots.hasFailed 0226 Layout.maximumWidth: headerLayout.width 0227 Layout.alignment: Qt.AlignHCenter 0228 Layout.preferredHeight: Math.min(Kirigami.Units.gridUnit * 20, Window.height * 0.25) 0229 0230 ScrollBar.vertical.policy: ScrollBar.AlwaysOff 0231 0232 ApplicationScreenshots { 0233 id: screenshots 0234 resource: appInfo.application 0235 delegateHeight: parent.Layout.preferredHeight * 0.8 0236 showNavigationArrows: screenshotsScroll.width === headerLayout.width 0237 } 0238 } 0239 0240 // Metadata 0241 Flow { 0242 id: metadataLayout 0243 readonly property int itemWidth: Kirigami.Units.gridUnit * 7 0244 readonly property int visibleChildren: countVisibleChildren(children) 0245 function countVisibleChildren(items) { 0246 let ret = 0; 0247 for (const itemPos in items) { 0248 const item = items[itemPos]; 0249 ret += item.visible; 0250 } 0251 return ret; 0252 } 0253 0254 // This centers the Flow in the page, no matter how many items have 0255 // flowed onto other rows 0256 Layout.maximumWidth: ((itemWidth + spacing) * Math.min(visibleChildren, Math.floor(headerLayout.width / (itemWidth + spacing)))) - spacing 0257 Layout.fillWidth: true 0258 Layout.alignment: Qt.AlignHCenter 0259 Layout.leftMargin: appInfo.internalSpacings 0260 Layout.rightMargin: appInfo.internalSpacings 0261 0262 spacing: Kirigami.Units.smallSpacing 0263 0264 // Not relevant to the offline upgrade use case 0265 visible: !appInfo.isOfflineUpgrade 0266 0267 onImplicitWidthChanged: visibleChildrenChanged() 0268 0269 // Version 0270 ColumnLayout { 0271 id: versionColumn 0272 width: metadataLayout.itemWidth 0273 0274 spacing: Kirigami.Units.smallSpacing 0275 0276 Label { 0277 Layout.fillWidth: true 0278 opacity: 0.7 0279 text: i18n("Version") 0280 horizontalAlignment: Text.AlignHCenter 0281 verticalAlignment: Text.AlignBottom 0282 wrapMode: Text.Wrap 0283 maximumLineCount: 2 0284 } 0285 Label { 0286 Layout.fillWidth: true 0287 text: appInfo.application.versionString 0288 horizontalAlignment: Text.AlignHCenter 0289 verticalAlignment: Text.AlignTop 0290 wrapMode: Text.Wrap 0291 maximumLineCount: 3 0292 elide: Text.ElideRight 0293 } 0294 } 0295 0296 // Size 0297 ColumnLayout { 0298 id: sizeColumn 0299 width: metadataLayout.itemWidth 0300 0301 spacing: Kirigami.Units.smallSpacing 0302 0303 Label { 0304 Layout.fillWidth: true 0305 opacity: 0.7 0306 text: i18n("Size") 0307 horizontalAlignment: Text.AlignHCenter 0308 verticalAlignment: Text.AlignBottom 0309 wrapMode: Text.Wrap 0310 maximumLineCount: 2 0311 } 0312 Label { 0313 Layout.fillWidth: true 0314 text: appInfo.application.sizeDescription 0315 horizontalAlignment: Text.AlignHCenter 0316 verticalAlignment: Text.AlignTop 0317 wrapMode: Text.Wrap 0318 maximumLineCount: 3 0319 elide: Text.ElideRight 0320 } 0321 } 0322 0323 // Distributor 0324 ColumnLayout { 0325 id: distributorColumn 0326 width: metadataLayout.itemWidth 0327 0328 spacing: Kirigami.Units.smallSpacing 0329 0330 Label { 0331 Layout.fillWidth: true 0332 opacity: 0.7 0333 text: i18n("Distributed by") 0334 horizontalAlignment: Text.AlignHCenter 0335 verticalAlignment: Text.AlignBottom 0336 wrapMode: Text.Wrap 0337 maximumLineCount: 2 0338 } 0339 Label { 0340 Layout.fillWidth: true 0341 text: appInfo.application.displayOrigin 0342 horizontalAlignment: Text.AlignHCenter 0343 verticalAlignment: Text.AlignTop 0344 wrapMode: Text.Wrap 0345 maximumLineCount: 3 0346 elide: Text.ElideRight 0347 } 0348 } 0349 0350 // License(s) 0351 ColumnLayout { 0352 id: licenseColumn 0353 width: metadataLayout.itemWidth 0354 0355 spacing: Kirigami.Units.smallSpacing 0356 0357 Label { 0358 Layout.fillWidth: true 0359 opacity: 0.7 0360 text: i18np("License", "Licenses", appInfo.application.licenses.length) 0361 horizontalAlignment: Text.AlignHCenter 0362 verticalAlignment: Text.AlignBottom 0363 wrapMode: Text.Wrap 0364 maximumLineCount: 2 0365 } 0366 Label { 0367 Layout.fillWidth: true 0368 visible : appInfo.application.licenses.length === 0 0369 text: i18nc("The app does not provide any licenses", "Unknown") 0370 horizontalAlignment: Text.AlignHCenter 0371 verticalAlignment: Text.AlignTop 0372 wrapMode: Text.Wrap 0373 maximumLineCount: 3 0374 elide: Text.ElideRight 0375 } 0376 ColumnLayout { 0377 Layout.fillWidth: true 0378 visible : appInfo.application.licenses.length > 0 0379 spacing: 0 0380 0381 Repeater { 0382 model: appInfo.application.licenses.slice(0, 2) 0383 delegate: RowLayout { 0384 Layout.fillWidth: true 0385 spacing: 0 0386 0387 Kirigami.UrlButton { 0388 Layout.fillWidth: true 0389 enabled: url !== "" 0390 text: modelData.name 0391 url: modelData.url 0392 horizontalAlignment: Text.AlignHCenter 0393 verticalAlignment: Text.AlignTop 0394 wrapMode: Text.Wrap 0395 maximumLineCount: 3 0396 elide: Text.ElideRight 0397 color: !modelData.hasFreedom ? Kirigami.Theme.neutralTextColor: enabled ? Kirigami.Theme.linkColor : Kirigami.Theme.textColor 0398 } 0399 0400 // Button to open "What's the risk of proprietary software?" sheet 0401 ToolButton { 0402 visible: !modelData.hasFreedom 0403 icon.name: "help-contextual" 0404 onClicked: properietarySoftwareRiskExplanationDialog.open(); 0405 0406 ToolTip { 0407 text: i18n("What does this mean?") 0408 } 0409 } 0410 } 0411 } 0412 0413 // "See More licenses" link, in case there are a lot of them 0414 Kirigami.LinkButton { 0415 Layout.fillWidth: true 0416 visible: application.licenses.length > 3 0417 text: i18np("See more…", "See more…", appInfo.application.licenses.length) 0418 horizontalAlignment: Text.AlignHCenter 0419 verticalAlignment: Text.AlignTop 0420 elide: Text.ElideRight 0421 onClicked: allLicensesSheet.open(); 0422 } 0423 0424 } 0425 } 0426 // Content Rating 0427 ColumnLayout { 0428 width: metadataLayout.itemWidth 0429 visible: appInfo.application.contentRatingText.length > 0 0430 spacing: Kirigami.Units.smallSpacing 0431 0432 Label { 0433 Layout.fillWidth: true 0434 opacity: 0.7 0435 text: i18n("Content Rating") 0436 horizontalAlignment: Text.AlignHCenter 0437 verticalAlignment: Text.AlignBottom 0438 wrapMode: Text.Wrap 0439 maximumLineCount: 2 0440 } 0441 0442 Label { 0443 Layout.fillWidth: true 0444 visible: text.length > 0 0445 text: appInfo.application.contentRatingMinimumAge === 0 ? "" : i18n("Age: %1+", appInfo.application.contentRatingMinimumAge) 0446 horizontalAlignment: Text.AlignHCenter 0447 verticalAlignment: Text.AlignTop 0448 wrapMode: Text.Wrap 0449 maximumLineCount: 3 0450 elide: Text.ElideRight 0451 } 0452 0453 Label { 0454 Layout.fillWidth: true 0455 text: appInfo.application.contentRatingText 0456 horizontalAlignment: Text.AlignHCenter 0457 verticalAlignment: Text.AlignTop 0458 wrapMode: Text.Wrap 0459 maximumLineCount: 3 0460 elide: Text.ElideRight 0461 0462 readonly property var colors: [ Kirigami.Theme.textColor, Kirigami.Theme.neutralTextColor ] 0463 color: colors[appInfo.application.contentRatingIntensity] 0464 } 0465 0466 Kirigami.LinkButton { 0467 Layout.fillWidth: true 0468 visible: appInfo.application.contentRatingDescription.length > 0 0469 text: i18nc("@action", "See details…") 0470 horizontalAlignment: Text.AlignHCenter 0471 verticalAlignment: Text.AlignTop 0472 elide: Text.ElideRight 0473 onClicked: contentRatingDialog.open(); 0474 } 0475 } 0476 } 0477 } 0478 0479 Kirigami.Separator { 0480 anchors { 0481 left: parent.left 0482 right: parent.right 0483 top: parent.bottom 0484 } 0485 } 0486 } 0487 0488 0489 Repeater { 0490 Layout.bottomMargin: appInfo.internalSpacings * 2 0491 0492 model: application.topObjects 0493 delegate: Loader { 0494 property QtObject resource: appInfo.application 0495 0496 Layout.fillWidth: item.Layout.fillWidth 0497 Layout.leftMargin: appInfo.pageContentMargins 0498 Layout.rightMargin: appInfo.pageContentMargins 0499 0500 source: modelData 0501 } 0502 } 0503 0504 // Layout for textual content; this isn't in the main ColumnLayout 0505 // because we want it to be bounded to a maximum width 0506 ColumnLayout { 0507 id: textualContentLayout 0508 0509 Layout.fillWidth: true 0510 Layout.maximumWidth: Math.max(Math.round(appInfo.width * 0.75), Kirigami.Units.gridUnit * 35) 0511 Layout.margins: appInfo.pageContentMargins 0512 Layout.alignment: Qt.AlignHCenter 0513 0514 spacing: appInfo.internalSpacings 0515 0516 // Short description 0517 // Not using Kirigami.Heading here because that component doesn't 0518 // support selectable text, and we want this to be selectable because 0519 // it's also used to show the path for local packages, and that makes 0520 // sense to be selectable 0521 Kirigami.SelectableLabel { 0522 Layout.fillWidth: true 0523 // Not relevant to the offline upgrade use case because we 0524 // display the info in the header instead 0525 visible: !appInfo.isOfflineUpgrade 0526 text: appInfo.application.comment 0527 wrapMode: Text.Wrap 0528 0529 // Match `level: 2` in Kirigami.Heading 0530 font.pointSize: Kirigami.Theme.defaultFont.pointSize * 1.2 0531 // Match `type: Kirigami.Heading.Type.Primary` in Kirigami.Heading 0532 font.weight: Font.DemiBold 0533 0534 Accessible.role: Accessible.Heading 0535 } 0536 0537 // Long app description 0538 Kirigami.SelectableLabel { 0539 Layout.fillWidth: true 0540 wrapMode: Text.WordWrap 0541 text: appInfo.application.longDescription 0542 textFormat: TextEdit.RichText 0543 onLinkActivated: Qt.openUrlExternally(link); 0544 } 0545 0546 // External resources 0547 GridLayout { 0548 id: externalResourcesLayout 0549 readonly property int visibleButtons: (helpButton.visible ? 1 : 0) 0550 + (homepageButton.visible ? 1: 0) 0551 + (addonsButton.visible ? 1 : 0) 0552 + (shareButton.visible ? 1 : 0) 0553 readonly property int buttonWidth: Math.round(textualContentLayout.width / columns) 0554 readonly property int tallestButtonHeight: Math.max(helpButton.implicitHeight, 0555 homepageButton.implicitHeight, 0556 shareButton.implicitHeight, 0557 addonsButton.implicitHeight) 0558 readonly property int minWidth: Math.max(helpButton.visible ? helpButton.implicitMinWidth : 0, 0559 homepageButton.visible ? homepageButton.implicitMinWidth: 0, 0560 addonsButton.visible ? addonsButton.implicitMinWidth : 0, 0561 shareButton.visible ? shareButton.implicitMinWidth : 0) 0562 readonly property bool stackedlayout: minWidth > Math.round(textualContentLayout.width / visibleButtons) - 0563 (columnSpacing * (visibleButtons + 1)) 0564 0565 Layout.fillWidth: true 0566 Layout.bottomMargin: appInfo.internalSpacings * 2 0567 0568 0569 rows: stackedlayout ? visibleButtons : 1 0570 columns: stackedlayout ? 1: visibleButtons 0571 rowSpacing: Kirigami.Units.smallSpacing 0572 columnSpacing: Kirigami.Units.smallSpacing 0573 0574 visible: visibleButtons > 0 0575 0576 ApplicationResourceButton { 0577 id: helpButton 0578 0579 Layout.fillWidth: true 0580 Layout.maximumWidth: externalResourcesLayout.buttonWidth 0581 Layout.minimumHeight: externalResourcesLayout.tallestButtonHeight 0582 0583 visible: application.helpURL != "" 0584 0585 buttonIcon: "documentation" 0586 title: i18n("Documentation") 0587 subtitle: i18n("Read the project's official documentation") 0588 tooltipText: application.helpURL 0589 0590 onClicked: Qt.openUrlExternally(application.helpURL); 0591 } 0592 0593 ApplicationResourceButton { 0594 id: homepageButton 0595 0596 Layout.fillWidth: true 0597 Layout.maximumWidth: externalResourcesLayout.buttonWidth 0598 Layout.minimumHeight: externalResourcesLayout.tallestButtonHeight 0599 0600 visible: application.homepage != "" 0601 0602 buttonIcon: "internet-services" 0603 title: i18n("Website") 0604 subtitle: i18n("Visit the project's website") 0605 tooltipText: application.homepage 0606 0607 onClicked: Qt.openUrlExternally(application.homepage); 0608 } 0609 0610 ApplicationResourceButton { 0611 id: addonsButton 0612 0613 Layout.fillWidth: true 0614 Layout.maximumWidth: externalResourcesLayout.buttonWidth 0615 Layout.minimumHeight: externalResourcesLayout.tallestButtonHeight 0616 0617 visible: addonsView.containsAddons 0618 0619 buttonIcon: "extension-symbolic" 0620 title: i18n("Addons") 0621 subtitle: i18n("Install or remove additional functionality") 0622 0623 onClicked: { 0624 if (addonsView.addonsCount === 0) { 0625 Navigation.openExtends(application.appstreamId, appInfo.application.name) 0626 } else { 0627 addonsView.sheetOpen = true 0628 } 0629 } 0630 } 0631 0632 ApplicationResourceButton { 0633 id: shareButton 0634 0635 Layout.fillWidth: true 0636 Layout.maximumWidth: externalResourcesLayout.buttonWidth 0637 Layout.minimumHeight: externalResourcesLayout.tallestButtonHeight 0638 0639 buttonIcon: "document-share" 0640 title: i18nc("Exports the application's URL to an external service", "Share") 0641 subtitle: i18n("Send a link for this application") 0642 tooltipText: application.url.toString() 0643 visible: tooltipText.length > 0 && !appInfo.isOfflineUpgrade 0644 0645 Kirigami.PromptDialog { 0646 id: shareSheet 0647 parent: applicationWindow().overlay 0648 title: shareButton.title 0649 standardButtons: Dialog.NoButton 0650 0651 Purpose.AlternativesView { 0652 id: alts 0653 implicitWidth: Kirigami.Units.gridUnit 0654 pluginType: "ShareUrl" 0655 inputData: { 0656 "urls": [ application.url.toString() ], 0657 "title": i18nc("The subject line for an email. %1 is the name of an application", "Check out the %1 app!", application.name) 0658 } 0659 onFinished: { 0660 shareSheet.close() 0661 if (error !== 0) { 0662 console.error("job finished with error", error, message) 0663 } 0664 alts.reset() 0665 } 0666 } 0667 } 0668 0669 onClicked: { 0670 shareSheet.open(); 0671 } 0672 } 0673 } 0674 0675 Kirigami.Heading { 0676 visible: changelogLabel.visible 0677 text: i18n("What's New") 0678 level: 2 0679 type: Kirigami.Heading.Type.Primary 0680 wrapMode: Text.Wrap 0681 } 0682 0683 // Changelog text 0684 Label { 0685 id: changelogLabel 0686 0687 Layout.fillWidth: true 0688 Layout.bottomMargin: appInfo.internalSpacings * 2 0689 0690 // Some backends are known to produce empty line break as a text 0691 visible: text !== "" && text !== "<br />" 0692 wrapMode: Text.WordWrap 0693 0694 Component.onCompleted: appInfo.application.fetchChangelog() 0695 Connections { 0696 target: appInfo.application 0697 function onChangelogFetched(changelog) { 0698 changelogLabel.text = changelog 0699 } 0700 } 0701 } 0702 0703 Kirigami.LoadingPlaceholder { 0704 Layout.alignment: Qt.AlignHCenter 0705 Layout.maximumWidth: Kirigami.Units.gridUnit * 15 0706 Layout.bottomMargin: appInfo.internalSpacings * 2 0707 visible: reviewsModel.fetching && !reviewsError.visible 0708 text: i18n("Loading reviews for %1", appInfo.application.name) 0709 } 0710 0711 Kirigami.Heading { 0712 Layout.fillWidth: true 0713 visible: rep.count > 0 || reviewsError.visible 0714 text: i18n("Reviews") 0715 level: 2 0716 type: Kirigami.Heading.Type.Primary 0717 wrapMode: Text.Wrap 0718 } 0719 0720 Kirigami.InlineMessage { 0721 id: reviewsError 0722 type: Kirigami.MessageType.Warning 0723 Layout.fillWidth: true 0724 visible: reviewsModel.backend && text.length > 0 0725 text: reviewsModel.backend ? reviewsModel.backend.errorMessage : "" 0726 } 0727 0728 // Top three reviews 0729 Repeater { 0730 id: rep 0731 model: PaginateModel { 0732 sourceModel: reviewsSheet.model 0733 pageSize: visibleReviews 0734 } 0735 delegate: ReviewDelegate { 0736 Layout.fillWidth: true 0737 separator: false 0738 compact: true 0739 } 0740 } 0741 0742 // Review-related buttons 0743 Flow { 0744 Layout.fillWidth: true 0745 Layout.bottomMargin: appInfo.internalSpacings *2 0746 0747 spacing: appInfo.internalSpacings 0748 0749 Button { 0750 visible: reviewsModel.count > visibleReviews 0751 0752 text: i18np("Show all %1 Reviews", "Show all %1 Reviews", reviewsModel.count) 0753 icon.name: "view-visible" 0754 0755 onClicked: { 0756 reviewsSheet.open() 0757 } 0758 } 0759 0760 Button { 0761 visible: appbutton.isStateAvailable && reviewsModel.backend && !reviewsError.visible && reviewsModel.backend.isResourceSupported(appInfo.application) 0762 enabled: appInfo.application.isInstalled 0763 0764 text: appInfo.application.isInstalled ? i18n("Write a Review") : i18n("Install to Write a Review") 0765 icon.name: "document-edit" 0766 0767 onClicked: { 0768 reviewsSheet.openReviewDialog() 0769 } 0770 } 0771 } 0772 0773 // "Get Involved" section 0774 Kirigami.Heading { 0775 visible: getInvolvedLayout.visible 0776 text: i18n("Get Involved") 0777 level: 2 0778 type: Kirigami.Heading.Type.Primary 0779 wrapMode: Text.Wrap 0780 } 0781 0782 GridLayout { 0783 id: getInvolvedLayout 0784 0785 readonly property int visibleButtons: (donateButton.visible ? 1 : 0) 0786 + (bugButton.visible ? 1 : 0) 0787 + (contributeButton.visible ? 1 : 0) 0788 readonly property int buttonWidth: Math.round(textualContentLayout.width / columns) 0789 readonly property int tallestButtonHeight: Math.max(donateButton.implicitHeight, 0790 bugButton.implicitHeight, 0791 contributeButton.implicitHeight) 0792 readonly property int minWidth: Math.max(donateButton.visible ? donateButton.implicitMinWidth : 0, 0793 bugButton.visible ? bugButton.implicitMinWidth: 0, 0794 contributeButton.visible ? contributeButton.implicitMinWidth : 0) 0795 readonly property bool stackedlayout: minWidth > Math.round(textualContentLayout.width / visibleButtons) - 0796 (columnSpacing * (visibleButtons + 1)) 0797 0798 Layout.fillWidth: true 0799 Layout.bottomMargin: appInfo.internalSpacings * 2 0800 0801 rows: stackedlayout ? visibleButtons : 1 0802 columns: stackedlayout ? 1: visibleButtons 0803 rowSpacing: Kirigami.Units.smallSpacing 0804 columnSpacing: Kirigami.Units.smallSpacing 0805 0806 visible: visibleButtons > 0 0807 0808 ApplicationResourceButton { 0809 id: donateButton 0810 0811 Layout.fillWidth: true 0812 Layout.maximumWidth: getInvolvedLayout.buttonWidth 0813 Layout.minimumHeight: getInvolvedLayout.tallestButtonHeight 0814 0815 visible: application.donationURL != "" 0816 0817 buttonIcon: "help-donate" 0818 title: i18n("Donate") 0819 subtitle: i18n("Support and thank the developers by donating to their project") 0820 tooltipText: application.donationURL 0821 0822 onClicked: Qt.openUrlExternally(application.donationURL); 0823 } 0824 0825 ApplicationResourceButton { 0826 id: bugButton 0827 0828 Layout.fillWidth: true 0829 Layout.maximumWidth: getInvolvedLayout.buttonWidth 0830 Layout.minimumHeight: getInvolvedLayout.tallestButtonHeight 0831 0832 visible: application.bugURL != "" 0833 0834 buttonIcon: "tools-report-bug" 0835 title: i18n("Report Bug") 0836 subtitle: i18n("Log an issue you found to help get it fixed") 0837 tooltipText: application.bugURL 0838 0839 onClicked: Qt.openUrlExternally(application.bugURL); 0840 } 0841 0842 ApplicationResourceButton { 0843 id: contributeButton 0844 0845 Layout.fillWidth: true 0846 Layout.maximumWidth: getInvolvedLayout.buttonWidth 0847 Layout.minimumHeight: getInvolvedLayout.tallestButtonHeight 0848 0849 visible: application.contributeURL != "" 0850 title: i18n("Contribute") 0851 subtitle: i18n("Help the developers by coding, designing, testing, or translating") 0852 tooltipText: application.contributeURL 0853 buttonIcon: "project-development" 0854 onClicked: Qt.openUrlExternally(application.contributeURL); 0855 } 0856 } 0857 0858 Repeater { 0859 model: application.objects 0860 delegate: Loader { 0861 property QtObject resource: appInfo.application 0862 source: modelData 0863 Layout.fillWidth: true 0864 } 0865 } 0866 } 0867 } 0868 0869 readonly property var addons: AddonsView { 0870 id: addonsView 0871 application: appInfo.application 0872 } 0873 0874 Kirigami.Dialog { 0875 id: allLicensesSheet 0876 title: i18n("All Licenses") 0877 standardButtons: Kirigami.Dialog.NoButton 0878 preferredWidth: Kirigami.Units.gridUnit * 16 0879 maximumHeight: Kirigami.Units.gridUnit * 20 0880 0881 ColumnLayout { 0882 spacing: 0 0883 0884 Repeater { 0885 id: listview 0886 0887 model: appInfo.application.licenses 0888 0889 delegate: Kirigami.BasicListItem { 0890 activeBackgroundColor: "transparent" 0891 activeTextColor: Kirigami.Theme.textColor 0892 separatorVisible: false 0893 contentItem: Kirigami.UrlButton { 0894 enabled: url !== "" 0895 text: modelData.name 0896 url: modelData.url 0897 horizontalAlignment: Text.AlignLeft 0898 color: !modelData.hasFreedom ? Kirigami.Theme.neutralTextColor: enabled ? Kirigami.Theme.linkColor : Kirigami.Theme.textColor 0899 } 0900 } 0901 } 0902 } 0903 } 0904 0905 Kirigami.PromptDialog { 0906 id: contentRatingDialog 0907 title: i18n("Content Rating") 0908 preferredWidth: Kirigami.Units.gridUnit * 25 0909 standardButtons: Kirigami.Dialog.NoButton 0910 0911 Label { 0912 text: appInfo.application.contentRatingDescription 0913 textFormat: Text.MarkdownText 0914 wrapMode: Text.Wrap 0915 } 0916 } 0917 0918 Kirigami.PromptDialog { 0919 id: properietarySoftwareRiskExplanationDialog 0920 preferredWidth: Kirigami.Units.gridUnit * 25 0921 standardButtons: Kirigami.Dialog.NoButton 0922 0923 title: i18n("Risks of proprietary software") 0924 0925 TextEdit { 0926 readonly property string proprietarySoftwareExplanationPage: "https://www.gnu.org/proprietary" 0927 0928 text: homepageButton.visible ? 0929 xi18nc("@info", "This application's source code is partially or entirely closed to public inspection and improvement. That means third parties and users like you cannot verify its operation, security, and trustworthiness, or modify and redistribute it without the authors' express permission.<nl/><nl/>The application may be perfectly safe to use, or it may be acting against you in various ways—such as harvesting your personal information, tracking your location, or transmitting the contents of your files to someone else. There is no easy way to be sure, so you should only install this application if you fully trust its authors (<link url='%1'>%2</link>).<nl/><nl/>You can learn more at <link url='%3'>%3</link>.", application.homepage, author.text, proprietarySoftwareExplanationPage) : 0930 xi18nc("@info", "This application's source code is partially or entirely closed to public inspection and improvement. That means third parties and users like you cannot verify its operation, security, and trustworthiness, or modify and redistribute it without the authors' express permission.<nl/><nl/>The application may be perfectly safe to use, or it may be acting against you in various ways—such as harvesting your personal information, tracking your location, or transmitting the contents of your files to someone else. There is no easy way to be sure, so you should only install this application if you fully trust its authors (%1).<nl/><nl/>You can learn more at <link url='%2'>%2</link>.", author.text, proprietarySoftwareExplanationPage) 0931 wrapMode: Text.Wrap 0932 textFormat: TextEdit.RichText 0933 readOnly: true 0934 0935 color: Kirigami.Theme.textColor 0936 selectedTextColor: Kirigami.Theme.highlightedTextColor 0937 selectionColor: Kirigami.Theme.highlightColor 0938 0939 onLinkActivated: (url) => Qt.openUrlExternally(url) 0940 0941 HoverHandler { 0942 acceptedButtons: Qt.NoButton 0943 cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor 0944 } 0945 } 0946 } 0947 }