diff --git a/discover/qml/ApplicationPage.qml b/discover/qml/ApplicationPage.qml index 836f05af..6cbc1b6f 100644 --- a/discover/qml/ApplicationPage.qml +++ b/discover/qml/ApplicationPage.qml @@ -1,348 +1,348 @@ /* * Copyright (C) 2012 Aleix Pol Gonzalez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library/Lesser General Public License * version 2, or (at your option) any later version, as published by the * Free Software Foundation * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library/Lesser General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.5 import QtQuick.Controls 1.1 import QtQuick.Controls 2.1 as QQC2 import QtQuick.Window 2.1 import QtQuick.Layouts 1.1 import org.kde.kquickcontrolsaddons 2.0 import org.kde.discover 2.0 import org.kde.discover.app 1.0 import org.kde.kirigami 2.1 as Kirigami import "navigation.js" as Navigation DiscoverPage { id: appInfo property QtObject application: null clip: true title: application.name background: Rectangle { color: Kirigami.Theme.viewBackgroundColor } ReviewsPage { id: reviewsSheet parentWidth: appInfo.width model: ReviewsModel { id: reviewsModel resource: appInfo.application } } - Keys.forwardTo: [screenshots] +// Keys.forwardTo: [screenshots] Kirigami.OverlaySheet { id: originsOverlay bottomPadding: Kirigami.Units.largeSpacing topPadding: Kirigami.Units.largeSpacing readonly property alias model: alternativeResourcesView.model function listBackends() { var first = true; var ret = ""; var m = alternativeResourcesView.model; for(var i=0, count=m.rowCount(); i" + res.displayOrigin + "" first = false } } return ret } readonly property string sentence: alternativeResourcesView.count <= 1 ? "" : i18n("\nAlso available in %1", listBackends()) ListView { id: alternativeResourcesView model: ResourcesProxyModel { allBackends: true resourcesUrl: appInfo.application.url } delegate: Kirigami.AbstractListItem { RowLayout { Label { text: displayOrigin } Label { Layout.fillWidth: true text: availableVersion elide: Text.ElideRight horizontalAlignment: Text.AlignRight } } checked: appInfo.application == model.application onClicked: if(index>=0) { var res = model.application console.assert(res) window.stack.pop() Navigation.openApplication(res) } } } } header: QQC2.ToolBar { anchors { right: parent.right left: parent.left } contentItem: RowLayout { spacing: Kirigami.Units.smallSpacing ToolButton { Layout.leftMargin: Kirigami.Units.smallSpacing iconName: "draw-arrow-back" tooltip: i18n("Back") enabled: appInfo.sClose.enabled onClicked: appInfo.sClose.activated() } Item { Layout.fillWidth: true } Kirigami.Heading { level: 3 Layout.fillWidth: true text: appInfo.application.name maximumLineCount: 1 elide: Text.ElideRight horizontalAlignment: Text.AlignHCenter } Item { Layout.fillWidth: true } Binding { target: appInfo.actions property: "main" value: appbutton.action } InstallApplicationButton { id: appbutton Layout.rightMargin: Kirigami.Units.smallSpacing application: appInfo.application visible: applicationWindow().wideScreen } Button { Layout.rightMargin: Kirigami.Units.smallSpacing visible: application.isInstalled && application.canExecute text: i18n("Launch") onClicked: application.invokeApplication() } } } leftPadding: Kirigami.Units.largeSpacing * (applicationWindow().wideScreen ? 2 : 1) rightPadding: Kirigami.Units.largeSpacing * (applicationWindow().wideScreen ? 2 : 1) // Icon, name, caption, screenshots, description and reviews ColumnLayout { spacing: 0 RowLayout { Kirigami.Icon { Layout.topMargin: Kirigami.Units.smallSpacing * 2 Layout.preferredHeight: 80 Layout.preferredWidth: 80 source: appInfo.application.icon Layout.rightMargin: Kirigami.Units.smallSpacing * 2 } ColumnLayout { spacing: 0 Kirigami.Heading { level: 1 text: appInfo.application.name lineHeight: 1.0 maximumLineCount: 1 elide: Text.ElideRight Layout.fillWidth: true Layout.alignment: Text.AlignBottom } Kirigami.Heading { level: 4 text: appInfo.application.comment maximumLineCount: 2 lineHeight: lineCount > 1 ? 0.75 : 1.2 elide: Text.ElideRight Layout.fillWidth: true Layout.alignment: Qt.AlignTop } } Layout.bottomMargin: Kirigami.Units.largeSpacing } - ScrollView { + ApplicationScreenshots { + id: screenshots + Layout.fillWidth: true + visible: count > 0 + resource: appInfo.application + QQC2.ScrollBar.horizontal: screenshotsScrollbar + } + QQC2.ScrollBar { + id: screenshotsScrollbar Layout.fillWidth: true - Layout.preferredHeight: Kirigami.Units.gridUnit * 13 - Layout.bottomMargin: Kirigami.Units.largeSpacing - visible: screenshots.count > 0 - ApplicationScreenshots { - id: screenshots - resource: appInfo.application - page: appInfo - - } } QQC2.Label { + Layout.topMargin: Kirigami.Units.largeSpacing Layout.fillWidth: true wrapMode: Text.WordWrap text: appInfo.application.longDescription + originsOverlay.sentence onLinkActivated: { var idx = parseInt(link, 10) var res = originsOverlay.model.resourceAt(idx) window.stack.pop() Navigation.openApplication(res) } } LinkButton { id: addonsButton text: i18n("Addons") visible: addonsView.containsAddons onClicked: addonsView.sheetOpen = true } LinkButton { text: i18n("Review") onClicked: reviewsSheet.openReviewDialog() visible: !commentsButton.visible && reviewsModel.backend && reviewsModel.backend.isResourceSupported(appInfo.application) } LinkButton { id: commentsButton readonly property QtObject rating: appInfo.application.rating visible: rating && rating.ratingCount>0 && reviewsModel.count text: i18n("Show reviews (%1)...", rating ? reviewsModel.count : 0) onClicked: { reviewsSheet.open() } } Item { height: addonsButton.height width: 5 } // Details/metadata Rectangle { Layout.topMargin: Kirigami.Units.smallSpacing color: Kirigami.Theme.linkColor Layout.fillWidth: true height: 1 Layout.bottomMargin: Kirigami.Units.smallSpacing } GridLayout { rowSpacing: 0 columns: 2 // Category row QQC2.Label { Layout.alignment: Qt.AlignRight text: i18n("Category:") } QQC2.Label { Layout.fillWidth: true elide: Text.ElideRight text: appInfo.application.categoryDisplay } // Version row QQC2.Label { readonly property string version: appInfo.application.isInstalled ? appInfo.application.installedVersion : appInfo.application.availableVersion visible: version.length > 0 Layout.alignment: Qt.AlignRight text: i18n("Version:") } QQC2.Label { readonly property string version: appInfo.application.isInstalled ? appInfo.application.installedVersion : appInfo.application.availableVersion visible: version.length > 0 Layout.fillWidth: true elide: Text.ElideRight text: version ? version : "" } // Size row QQC2.Label { Layout.alignment: Qt.AlignRight text: i18n("Size:") } QQC2.Label { Layout.fillWidth: true elide: Text.ElideRight text: i18n("%1", appInfo.application.sizeDescription) } // Source row QQC2.Label { Layout.alignment: Qt.AlignRight text: i18n("Source:") } LinkButton { Layout.fillWidth: true horizontalAlignment: Text.AlignLeft enabled: alternativeResourcesView.count > 1 text: appInfo.application.displayOrigin elide: Text.ElideRight onClicked: originsOverlay.open() } // License row QQC2.Label { Layout.alignment: Qt.AlignRight text: i18n("License:") visible: appInfo.application.license.length>0 } LinkButton { elide: Text.ElideRight Layout.fillWidth: true horizontalAlignment: Text.AlignLeft visible: text.length>0 text: appInfo.application.license // tooltip: i18n("See full license terms") onClicked: Qt.openUrlExternally("https://spdx.org/licenses/" + appInfo.application.license + ".html#licenseText") } // Homepage row QQC2.Label { readonly property string homepage: application.homepage visible: homepage.length > 0 Layout.alignment: Qt.AlignRight text: i18n("Homepage:") } LinkButton { readonly property string homepage: application.homepage visible: homepage.length > 0 text: homepage onClicked: Qt.openUrlExternally(application.homepage) elide: Text.ElideRight Layout.fillWidth: true horizontalAlignment: Text.AlignLeft } } } readonly property var addons: AddonsView { id: addonsView application: appInfo.application parent: overlay } } diff --git a/discover/qml/ApplicationScreenshots.qml b/discover/qml/ApplicationScreenshots.qml index 99d04f24..329b55c5 100644 --- a/discover/qml/ApplicationScreenshots.qml +++ b/discover/qml/ApplicationScreenshots.qml @@ -1,161 +1,173 @@ /* * Copyright (C) 2012 Aleix Pol Gonzalez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library/Lesser General Public License * version 2, or (at your option) any later version, as published by the * Free Software Foundation * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library/Lesser General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.1 +import QtQuick.Layouts 1.1 import QtQuick.Controls 1.1 import QtQuick.Controls 2.1 as QQC2 import QtGraphicalEffects 1.0 import org.kde.discover 2.0 import org.kde.kirigami 2.0 as Kirigami -ListView { +Flickable { id: root + readonly property alias count: screenshotsModel.count property alias resource: screenshotsModel.application - property QtObject page - - spacing: Kirigami.Units.largeSpacing - focus: overlay.visible - - Keys.onLeftPressed: if (leftAction.visible) leftAction.trigger() - Keys.onRightPressed: if (rightAction.visible) rightAction.trigger() - orientation: ListView.Horizontal + property var resource + property int currentIndex: -1 + property Item currentItem: screenshotsRep.itemAt(currentIndex) + Layout.preferredHeight: Kirigami.Units.gridUnit * 13 + contentHeight: height + contentWidth: screenshotsLayout.width QQC2.Popup { id: overlay parent: applicationWindow().overlay modal: true x: (parent.width - width)/2 y: (parent.height - height)/2 readonly property real proportion: overlayImage.sourceSize.width>1 ? overlayImage.sourceSize.height/overlayImage.sourceSize.width : 1 height: Math.min(parent.height * 0.9, (parent.width * 0.9) * proportion, overlayImage.sourceSize.height) width: height/proportion Image { id: overlayImage anchors.fill: parent source: root.currentItem ? root.currentItem.imageSource : "" fillMode: Image.PreserveAspectFit smooth: true } Button { anchors { right: parent.left verticalCenter: parent.verticalCenter } visible: leftAction.visible iconName: leftAction.iconName onClicked: leftAction.triggered(null) } Button { anchors { left: parent.right verticalCenter: parent.verticalCenter } visible: rightAction.visible iconName: rightAction.iconName onClicked: rightAction.triggered(null) } Kirigami.Action { id: leftAction iconName: "arrow-left" enabled: overlay.visible && visible visible: root.currentIndex >= 1 - onTriggered: root.decrementCurrentIndex() + onTriggered: root.currentIndex = (root.currentIndex - 1) % screenshotsModel.count } Kirigami.Action { id: rightAction iconName: "arrow-right" enabled: overlay.visible && visible visible: root.currentIndex < (root.count - 1) - onTriggered: root.incrementCurrentIndex() + onTriggered: root.currentIndex = (root.currentIndex + 1) % screenshotsModel.count } } - model: ScreenshotsModel { - id: screenshotsModel - } + Row { + id: screenshotsLayout + height: root.contentHeight + spacing: Kirigami.Units.largeSpacing + focus: overlay.visible - delegate: MouseArea { - readonly property url imageSource: large_image_url - readonly property real proportion: thumbnail.sourceSize.width>1 ? thumbnail.sourceSize.height/thumbnail.sourceSize.width : 1 - width: Math.max(50, height/proportion) - height: parent.height + Keys.onLeftPressed: if (leftAction.visible) leftAction.trigger() + Keys.onRightPressed: if (rightAction.visible) rightAction.trigger() - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - - onClicked: { - root.currentIndex = index - overlay.open() - } - DropShadow { - source: thumbnail - anchors.fill: thumbnail - verticalOffset: 3 - horizontalOffset: 0 - radius: 12.0 - samples: 25 - color: Kirigami.Theme.disabledTextColor - cached: true - } + Repeater { + id: screenshotsRep + model: ScreenshotsModel { + id: screenshotsModel + } - BusyIndicator { - visible: running - running: parent.status == Image.Loading - anchors.centerIn: thumbnail + delegate: MouseArea { + readonly property url imageSource: large_image_url + readonly property real proportion: thumbnail.sourceSize.width>1 ? thumbnail.sourceSize.height/thumbnail.sourceSize.width : 1 + width: Math.max(50, height/proportion) + height: screenshotsLayout.height + + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + + onClicked: { + root.currentIndex = index + overlay.open() + } + + DropShadow { + source: thumbnail + anchors.fill: thumbnail + verticalOffset: 3 + horizontalOffset: 0 + radius: 12.0 + samples: 25 + color: Kirigami.Theme.disabledTextColor + cached: true + } + + BusyIndicator { + visible: running + running: thumbnail.status == Image.Loading + anchors.centerIn: thumbnail + } + + Image { + id: thumbnail + source: small_image_url + height: parent.height + fillMode: Image.PreserveAspectFit + smooth: true + } + } } - - Image { - id: thumbnail - source: small_image_url - height: parent.height - fillMode: Image.PreserveAspectFit - smooth: true + } + clip: true + readonly property var leftShadow: Shadow { + parent: root + anchors { + left: parent.left + top: parent.top + bottom: parent.bottom } - + edge: Qt.LeftEdge + width: Math.max(0, Math.min(root.width/5, root.contentX)) } - - layer.enabled: true - // This item should be used as the 'mask' - layer.effect: ShaderEffect { - readonly property var colorSource: root; - readonly property real distLeft: Math.max(20, 2000/(root.contentX + 1) - 1) - readonly property real distRight: Math.max(20, 2000/Math.max(1, root.contentWidth - (root.contentX + root.width) + 1) - 1) - fragmentShader: " - uniform lowp float distLeft; - uniform lowp float distRight; - uniform lowp sampler2D colorSource; - uniform lowp float qt_Opacity; - varying highp vec2 qt_TexCoord0; - void main() { - gl_FragColor = - texture2D(colorSource, qt_TexCoord0) - * clamp(qt_TexCoord0.x * -distRight + distRight, 0., 1.) - * clamp(qt_TexCoord0.x * distLeft, 0., 1.) - * qt_Opacity; - } - " + readonly property var rightShadow: Shadow { + parent: root + anchors { + right: parent.right + top: parent.top + bottom: parent.bottom + } + edge: Qt.RightEdge + width: Math.max(0, Math.min(root.contentWidth - root.contentX - root.width)/5) } } diff --git a/discover/resources.qrc b/discover/resources.qrc index 8387ffdb..1e543b99 100644 --- a/discover/resources.qrc +++ b/discover/resources.qrc @@ -1,39 +1,40 @@ qml/TopLevelPageData.qml qml/CategoryDisplay.qml qml/ApplicationsListPage.qml qml/ApplicationPage.qml qml/ReviewsPage.qml qml/AddonsView.qml qml/ApplicationDelegate.qml qml/InstallApplicationButton.qml qml/Rating.qml qml/UpdatesPage.qml qml/ReviewDialog.qml qml/ProgressView.qml qml/BrowsingPage.qml qml/InstalledPage.qml qml/SearchPage.qml qml/Information.qml qml/SourcesPage.qml qml/ReviewDelegate.qml qml/AddSourceDialog.qml qml/ConditionalLoader.qml qml/LinkButton.qml qml/ApplicationScreenshots.qml qml/LabelBackground.qml qml/PageHeader.qml qml/ActionBridge.qml qml/DiscoverSystemPalette.qml qml/DiscoverPage.qml qml/DiscoverWindow.qml qml/DiscoverDrawer.qml qml/ActionListItem.qml qml/LoadingPage.qml qml/SearchField.qml + qml/Shadow.qml qml/navigation.js