Warning, /plasma/discover/discover/qml/DiscoverWindow.qml is written in an unsupported language. File is not indexed.

0001 import QtQuick 2.15
0002 import QtQuick.Layouts 1.15
0003 import QtQuick.Controls 2.14
0004 import QtQml.Models 2.15
0005 import org.kde.discover 2.0
0006 import org.kde.discover.app 1.0
0007 import org.kde.kquickcontrolsaddons 2.0
0008 import org.kde.kirigami 2.19 as Kirigami
0009 import "navigation.js" as Navigation
0010 
0011 Kirigami.ApplicationWindow
0012 {
0013     id: window
0014     readonly property string topBrowsingComp: ("qrc:/qml/BrowsingPage.qml")
0015     readonly property string topInstalledComp: ("qrc:/qml/InstalledPage.qml")
0016     readonly property string topSearchComp: ("qrc:/qml/SearchPage.qml")
0017     readonly property string topUpdateComp: ("qrc:/qml/UpdatesPage.qml")
0018     readonly property string topSourcesComp: ("qrc:/qml/SourcesPage.qml")
0019     readonly property string topAboutComp: ("qrc:/qml/AboutPage.qml")
0020     readonly property QtObject stack: window.pageStack
0021     property string currentTopLevel
0022 
0023     objectName: "DiscoverMainWindow"
0024     title: leftPage ? leftPage.title : ""
0025 
0026     width: app.initialGeometry.width>=10 ? app.initialGeometry.width : Kirigami.Units.gridUnit * 52
0027     height: app.initialGeometry.height>=10 ? app.initialGeometry.height : Math.max(Kirigami.Units.gridUnit * 38, window.globalDrawer.contentHeight)
0028 
0029     visible: true
0030 
0031     minimumWidth: Kirigami.Units.gridUnit * 17
0032     minimumHeight: Kirigami.Units.gridUnit * 17
0033 
0034     pageStack.defaultColumnWidth: Math.max(Kirigami.Units.gridUnit * 25, pageStack.width / 4)
0035     pageStack.globalToolBar.style: Kirigami.Settings.isMobile ? Kirigami.ApplicationHeaderStyle.Titles : Kirigami.ApplicationHeaderStyle.Auto
0036     pageStack.globalToolBar.showNavigationButtons: pageStack.currentIndex == 0 ? Kirigami.ApplicationHeaderStyle.None : Kirigami.ApplicationHeaderStyle.ShowBackButton
0037     pageStack.globalToolBar.canContainHandles: true // mobile handles in header
0038 
0039     readonly property var leftPage: window.stack.depth>0 ? window.stack.get(0) : null
0040 
0041     Component.onCompleted: {
0042         if (app.isRoot) {
0043             messagesSheet.addMessage(i18n("Running as <em>root</em> is discouraged and unnecessary."));
0044         }
0045     }
0046 
0047     readonly property string describeSources: feedbackLoader.item ? feedbackLoader.item.describeDataSources : ""
0048     Loader {
0049         id: feedbackLoader
0050         source: "Feedback.qml"
0051     }
0052 
0053     TopLevelPageData {
0054         id: featuredAction
0055         iconName: "go-home"
0056         text: i18n("&Home")
0057         component: topBrowsingComp
0058         objectName: "discover"
0059     }
0060 
0061     TopLevelPageData {
0062         id: searchAction
0063         visible: enabled
0064         enabled: !window.wideScreen
0065         iconName: "search"
0066         text: i18n("&Search")
0067         component: topSearchComp
0068         objectName: "search"
0069         shortcut: StandardKey.Find
0070     }
0071     TopLevelPageData {
0072         id: installedAction
0073         iconName: "view-list-details"
0074         text: i18n("&Installed")
0075         component: topInstalledComp
0076         objectName: "installed"
0077     }
0078     TopLevelPageData {
0079         id: updateAction
0080         iconName: ResourcesModel.updatesCount>0 ? ResourcesModel.hasSecurityUpdates ? "update-high" : "update-low" : "update-none"
0081         text: ResourcesModel.updatesCount<=0 ? (ResourcesModel.isFetching ? i18n("Fetching &updates…") : i18n("&Up to date") ) : i18nc("Update section name", "&Update (%1)", ResourcesModel.updatesCount)
0082         component: topUpdateComp
0083         objectName: "update"
0084     }
0085     TopLevelPageData {
0086         id: aboutAction
0087         iconName: "help-feedback"
0088         text: i18n("&About")
0089         component: topAboutComp
0090         objectName: "about"
0091         shortcut: StandardKey.HelpContents
0092     }
0093     TopLevelPageData {
0094         id: sourcesAction
0095         iconName: "configure"
0096         text: i18n("S&ettings")
0097         component: topSourcesComp
0098         objectName: "sources"
0099         shortcut: StandardKey.Preferences
0100     }
0101 
0102     Kirigami.Action {
0103         id: refreshAction
0104         readonly property QtObject action: ResourcesModel.updateAction
0105         text: action.text
0106         icon.name: "view-refresh"
0107         onTriggered: action.trigger()
0108         enabled: action.enabled
0109         // Don't need to show this action in mobile view since you can pull down
0110         // on the view to refresh, and this is the common and expected behavior
0111         //on that platform
0112         visible: window.wideScreen
0113         tooltip: shortcut.nativeText
0114 
0115         // Need to define an explicit Shortcut object so we can get its text
0116         // using shortcut.nativeText
0117         shortcut: Shortcut {
0118             sequences: [ StandardKey.Refresh ]
0119             onActivated: refreshAction.trigger()
0120         }
0121     }
0122 
0123     Connections {
0124         target: app
0125         function onOpenApplicationInternal(app) {
0126             Navigation.clearStack()
0127             Navigation.openApplication(app)
0128         }
0129         function onListMimeInternal(mime)  {
0130             currentTopLevel = topBrowsingComp;
0131             Navigation.openApplicationMime(mime)
0132         }
0133         function onListCategoryInternal(cat)  {
0134             currentTopLevel = topBrowsingComp;
0135             Navigation.openCategory(cat, "")
0136         }
0137 
0138         function onOpenSearch(search) {
0139             Navigation.clearStack()
0140             Navigation.openApplicationList({search: search})
0141         }
0142 
0143         function onOpenErrorPage(errorMessage, errorExplanation, buttonText, buttonIcon, buttonUrl) {
0144             Navigation.clearStack()
0145             console.warn("Error: " + errorMessage + "\n" + errorExplanation + "\n" + "Please visit " + buttonUrl)
0146             window.stack.push(errorPageComponent, { title: i18n("Error"), errorMessage: errorMessage, errorExplanation: errorExplanation, buttonText: buttonText, buttonIcon: buttonIcon, buttonUrl: buttonUrl })
0147         }
0148 
0149         function onUnableToFind(resid) {
0150             messagesSheet.addMessage(i18n("Unable to find resource: %1", resid));
0151             Navigation.openHome()
0152         }
0153     }
0154 
0155     Connections {
0156         target: ResourcesModel
0157 
0158         function onPassiveMessage(message) {
0159             messagesSheet.addMessage(message);
0160         }
0161     }
0162 
0163     footer: Loader {
0164         active: !window.wideScreen
0165         visible: active // ensure that no height is used when not loaded
0166         height: item ? item.implicitHeight : 0
0167         sourceComponent: Kirigami.NavigationTabBar {
0168             actions: [
0169                 featuredAction,
0170                 searchAction,
0171                 installedAction,
0172                 updateAction
0173             ]
0174             Component.onCompleted: {
0175                 // Exclusivity is already handled by the actions. This prevents BUG:448460
0176                 tabGroup.exclusive = false
0177             }
0178         }
0179     }
0180 
0181     Component {
0182         id: errorPageComponent
0183         Kirigami.Page {
0184             id: page
0185             property string errorMessage: ""
0186             property string errorExplanation: ""
0187             property string buttonText: ""
0188             property string buttonIcon: ""
0189             property string buttonUrl: ""
0190             readonly property bool isHome: true
0191             function searchFor(text) {
0192                 if (text.length === 0)
0193                     return;
0194                 Navigation.openCategory(null, "")
0195             }
0196             Kirigami.PlaceholderMessage {
0197                 anchors.centerIn: parent
0198                 width: parent.width - (Kirigami.Units.largeSpacing * 8)
0199                 visible: page.errorMessage !== ""
0200                 icon.name: "emblem-warning"
0201                 text: page.errorMessage
0202                 explanation: page.errorExplanation
0203                 helpfulAction: Kirigami.Action {
0204                     icon.name: page.buttonIcon
0205                     text: page.buttonText
0206                     onTriggered: {
0207                         Qt.openUrlExternally(page.buttonUrl)
0208                     }
0209                 }
0210             }
0211         }
0212     }
0213 
0214     Component {
0215         id: proceedDialog
0216         Kirigami.OverlaySheet {
0217             id: sheet
0218             showCloseButton: false
0219             property QtObject transaction
0220             property alias description: desc.text
0221             property bool acted: false
0222 
0223             // No need to add our own ScrollView since OverlaySheet includes
0224             // one automatically.
0225             // But we do need to put the label into a Layout of some sort so we
0226             // can limit the width of the sheet.
0227             contentItem: ColumnLayout {
0228                 Label {
0229                     id: desc
0230 
0231                     Layout.fillWidth: true
0232                     Layout.maximumWidth: Kirigami.Units.gridUnit * 20
0233 
0234                     textFormat: Text.StyledText
0235                     wrapMode: Text.WordWrap
0236                 }
0237             }
0238 
0239             footer: RowLayout {
0240 
0241                 Item { Layout.fillWidth : true }
0242 
0243                 Button {
0244                     text: i18n("Proceed")
0245                     icon.name: "dialog-ok"
0246                     onClicked: {
0247                         transaction.proceed()
0248                         sheet.acted = true
0249                         sheet.close()
0250                     }
0251                     Keys.onEnterPressed: clicked()
0252                     Keys.onReturnPressed: clicked()
0253                 }
0254 
0255                 Button {
0256                     Layout.alignment: Qt.AlignRight
0257                     text: i18n("Cancel")
0258                     icon.name: "dialog-cancel"
0259                     onClicked: {
0260                         transaction.cancel()
0261                         sheet.acted = true
0262                         sheet.close()
0263                     }
0264                     Keys.onEscapePressed: clicked()
0265                 }
0266             }
0267 
0268             onSheetOpenChanged: if(!sheetOpen) {
0269                 sheet.destroy(1000)
0270                 if (!sheet.acted)
0271                     transaction.cancel()
0272             }
0273         }
0274     }
0275 
0276     Component {
0277         id: distroErrorMessageDialog
0278         Kirigami.OverlaySheet {
0279             id: sheet
0280             property alias message: desc.text
0281 
0282             // No need to add our own ScrollView since OverlaySheet includes
0283             // one automatically.
0284             // But we do need to put the label into a Layout of some sort so we
0285             // can limit the width of the sheet.
0286             contentItem: ColumnLayout {
0287                 Label {
0288                     id: desc
0289 
0290                     Layout.fillWidth: true
0291                     Layout.maximumWidth: Kirigami.Units.gridUnit * 20
0292 
0293                     textFormat: Text.StyledText
0294                     wrapMode: Text.WordWrap
0295                 }
0296 
0297                 RowLayout {
0298                     Item { Layout.fillWidth : true }
0299                     Button {
0300                         icon.name: "tools-report-bug"
0301                         text: i18n("Report this issue")
0302                         onClicked: {
0303                             Qt.openUrlExternally(ResourcesModel.distroBugReportUrl())
0304                         }
0305                     }
0306                 }
0307             }
0308 
0309             onSheetOpenChanged: if(!sheetOpen) {
0310                 sheet.destroy(1000)
0311             }
0312         }
0313     }
0314 
0315     Kirigami.OverlaySheet {
0316         id: messagesSheet
0317 
0318         property bool copyButtonEnabled: true
0319 
0320         function addMessage(message: string) {
0321             messages.append({message: message});
0322             app.restore()
0323         }
0324 
0325         title: messages.count > 1 ? i18n("Error %1 of %2", messagesSheetView.currentIndex + 1, messages.count) : i18n("Error")
0326 
0327         // No need to add our own ScrollView since OverlaySheet includes
0328         // one automatically.
0329         // But we do need to put the label into a Layout of some sort so we
0330         // can limit the width of the sheet.
0331         contentItem: ColumnLayout {
0332             Item {
0333                 Layout.fillWidth: true
0334                 Layout.maximumWidth: Kirigami.Units.gridUnit * 20
0335             }
0336 
0337             StackLayout {
0338                 id: messagesSheetView
0339 
0340                 Layout.fillWidth: true
0341                 Layout.bottomMargin: Kirigami.Units.gridUnit
0342 
0343                 Repeater {
0344                     model: ListModel {
0345                         id: messages
0346 
0347                         onCountChanged: {
0348                             messagesSheet.sheetOpen = (count > 0);
0349 
0350                             if (count > 0 && messagesSheetView.currentIndex === -1) {
0351                                 messagesSheetView.currentIndex = 0;
0352                             }
0353                         }
0354                     }
0355 
0356                     delegate: Label {
0357                         Layout.fillWidth: true
0358 
0359                         text: model.message
0360                         textFormat: Text.StyledText
0361                         wrapMode: Text.WordWrap
0362                     }
0363                 }
0364             }
0365 
0366             RowLayout {
0367                 Layout.fillWidth: true
0368 
0369                 Button {
0370                     text: i18nc("@action:button", "Show Previous")
0371                     icon.name: "go-previous"
0372                     visible: messages.count > 1
0373                     enabled: visible && messagesSheetView.currentIndex > 0
0374 
0375                     onClicked: {
0376                         if (messagesSheetView.currentIndex > 0) {
0377                             messagesSheetView.currentIndex--;
0378                         }
0379                     }
0380                 }
0381 
0382                 Button {
0383                     text: i18nc("@action:button", "Show Next")
0384                     icon.name: "go-next"
0385                     visible: messages.count > 1
0386                     enabled: visible && messagesSheetView.currentIndex < messages.count - 1
0387 
0388                     onClicked: {
0389                         if (messagesSheetView.currentIndex < messages.count) {
0390                             messagesSheetView.currentIndex++;
0391                         }
0392                     }
0393                 }
0394 
0395                 Item { Layout.fillWidth: true }
0396 
0397                 Button {
0398                     Layout.alignment: Qt.AlignRight
0399                     text: i18n("Copy to Clipboard")
0400                     icon.name: "edit-copy"
0401 
0402                     onClicked: {
0403                         app.copyTextToClipboard(messages.get(messagesSheetView.currentIndex).message);
0404                     }
0405                 }
0406             }
0407         }
0408 
0409         onSheetOpenChanged: if (!sheetOpen) {
0410             messagesSheetView.currentIndex = -1;
0411             messages.clear();
0412         }
0413     }
0414 
0415     Instantiator {
0416         model: TransactionModel
0417 
0418         delegate: Connections {
0419             target: model.transaction ? model.transaction : null
0420 
0421             function onProceedRequest(title, description) {
0422                 var dialog = proceedDialog.createObject(window, {transaction: transaction, title: title, description: description})
0423                 dialog.open()
0424                 app.restore()
0425             }
0426 
0427             function onPassiveMessage(message) {
0428                 messagesSheet.addMessage(message);
0429             }
0430 
0431             function onDistroErrorMessage(message, actions) {
0432                 var dialog = distroErrorMessageDialog.createObject(window, {transaction: transaction, title: i18n("Error"), message: message})
0433                 dialog.open()
0434                 app.restore()
0435             }
0436             function onWebflowStarted(url) {
0437                 var component = Qt.createComponent("WebflowDialog.qml");
0438                 if (component.status == Component.Error) {
0439                     Qt.openUrlExternally(url);
0440                     console.error("Webflow Error", component.errorString())
0441                 } else if (component.status == Component.Ready) {
0442                     const sheet = component.createObject(window, {transaction: transaction, url: url });
0443                     sheet.open()
0444                 }
0445                 component.destroy();
0446             }
0447         }
0448     }
0449 
0450     PowerManagementInterface {
0451         reason: TransactionModel.mainTransactionText
0452         preventSleep: TransactionModel.count > 0
0453     }
0454 
0455     contextDrawer: Kirigami.ContextDrawer {}
0456 
0457     globalDrawer: DiscoverDrawer {
0458         wideScreen: window.wideScreen
0459     }
0460 
0461     onCurrentTopLevelChanged: {
0462         window.pageStack.clear()
0463         if (currentTopLevel)
0464             window.pageStack.push(currentTopLevel, {}, window.status!==Component.Ready)
0465         globalDrawer.forceSearchFieldFocus();
0466     }
0467 
0468     UnityLauncher {
0469         launcherId: "org.kde.discover.desktop"
0470         progressVisible: TransactionModel.count > 0
0471         progress: TransactionModel.progress
0472     }
0473 }