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

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