Files
qt-creator/share/qtcreator/qmldesigner/welcomepage/ThumbnailDelegate.qml

631 lines
22 KiB
QML
Raw Normal View History

// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import WelcomeScreen 1.0
import ExampleCheckout 1.0
Item {
id: root
width: Constants.thumbnailSize
height: Constants.thumbnailSize
clip: true
state: "normal"
property bool textWrapped: false
property bool hasDescription: false
property bool hasPath: false
property bool hasUpdate: false
property bool downloadable: false
property int numberOfPanels: 3
enum Type {
RecentProject,
Example,
Tutorial
}
property int type: ThumbnailDelegate.Type.RecentProject
property alias projectNameLabelText: projectNameLabel.text
property alias projectPathLabelText: projectPathLabel.text
property alias thumbnailPlaceholderSource: thumbnailPlaceholder.source
property alias downloadUrl: downloader.url
property alias tempFile: downloader.outputFile
property alias completeBaseName: downloader.completeBaseName
property alias targetPath: extractor.targetPath
property alias bannerLabelText: updateText.text
enum Panel {
InBetween,
Download,
Main,
Details
}
property int currentPanel: ThumbnailDelegate.Panel.Main
signal clicked()
signal rightClicked()
function startDownload() {
twirlToDownloadAnimation.start()
downloadPanel.text = downloadPanel.downloadText
downloadPanel.allowCancel = true
downloadPanel.value = Qt.binding(function() { return downloader.progress })
downloader.start()
mouseArea.enabled = false
}
function thumbnailClicked() {
if (root.type === ThumbnailDelegate.Type.RecentProject
|| root.type === ThumbnailDelegate.Type.Tutorial)
root.clicked() // open recent project/tutorial
if (Constants.loadingProgress < 95)
return
if (root.type === ThumbnailDelegate.Type.Example) {
if (root.downloadable
&& !downloadButton.alreadyDownloaded
&& !downloadButton.downloadUnavailable) {
root.startDownload()
} else if (downloadButton.alreadyDownloaded) {
root.clicked() // open example
}
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
propagateComposedEvents: true
hoverEnabled: true
acceptedButtons: Qt.LeftButton | Qt.RightButton
Connections {
target: mouseArea
function onClicked(mouse) {
if (mouse.button === Qt.RightButton)
root.rightClicked()
else
root.thumbnailClicked(mouse)
}
}
}
CustomDialog {
id: overwriteDialog
title: qsTr("Overwrite Example?")
standardButtons: Dialog.Yes | Dialog.No
modal: true
anchors.centerIn: parent
onAccepted: root.startDownload()
Text {
width: parent.width
horizontalAlignment: Text.AlignHCenter
font.pixelSize: 12
color: Constants.currentGlobalText
text: qsTr("Example already exists.<br>Do you want to replace it?")
}
}
Item {
id: canvas
width: Constants.thumbnailSize
height: Constants.thumbnailSize * root.numberOfPanels
DownloadPanel {
id: downloadPanel
height: Constants.thumbnailSize
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: mainPanel.top
radius: 10
value: downloader.progress
readonly property string downloadText: qsTr("Downloading...")
readonly property string extractText: qsTr("Extracting...")
onCancelRequested: downloader.cancel()
}
Rectangle {
id: mainPanel
height: Constants.thumbnailSize
anchors.left: parent.left
anchors.right: parent.right
color: "#ea8c8c"
radius: 10
property bool multiline: (projectNameLabelMetric.width >= projectNameLabel.width)
TextMetrics {
id: projectNameLabelMetric
text: projectNameLabel.text
font: projectNameLabel.font
}
Image {
id: thumbnailPlaceholder
anchors.fill: parent
anchors.bottomMargin: Constants.imageBottomMargin
anchors.rightMargin: Constants.thumbnailMargin
anchors.leftMargin: Constants.thumbnailMargin
anchors.topMargin: Constants.thumbnailMargin
fillMode: Image.PreserveAspectFit
verticalAlignment: Image.AlignTop
mipmap: true
}
Rectangle {
id: projectNameBackground
height: mainPanel.multiline && !root.hasPath ? Constants.titleHeightMultiline
: Constants.titleHeight
color: "#e5b0e4"
radius: 3
anchors.left: parent.left
anchors.right: parent.right
anchors.top: thumbnailPlaceholder.bottom
anchors.topMargin: Constants.titleBackgroundTopMargin
anchors.leftMargin: Constants.thumbnailMargin
anchors.rightMargin: Constants.thumbnailMargin
Text {
id: projectNameLabel
color: Constants.currentGlobalText
text: typeof(displayName) === "undefined" ? projectName : displayName
elide: root.hasPath ? Text.ElideMiddle : Text.ElideRight
font.pixelSize: 16
wrapMode: Text.WordWrap
maximumLineCount: 2
clip: false
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: Constants.titleMargin
anchors.rightMargin: Constants.titleMargin
MouseArea {
id: projectNameMouseArea
anchors.fill: parent
hoverEnabled: true
// Only enable the MouseArea if label actually contains text
enabled: projectNameLabel.text !== "" ? true : false
Connections {
target: projectNameMouseArea
onClicked: root.thumbnailClicked()
}
}
ToolTip {
id: projectNameToolTip
y: -projectNameToolTip.height
visible: projectNameMouseArea.containsMouse && projectNameLabel.truncated
text: projectNameLabel.text
delay: 1000
height: 20
background: Rectangle {
color: Constants.currentToolTipBackground
border.color: Constants.currentToolTipOutline
border.width: 1
}
contentItem: Text {
color: Constants.currentToolTipText
text: projectNameToolTip.text
verticalAlignment: Text.AlignVCenter
}
}
}
DownloadButton {
id: downloadButton
anchors.right: parent.right
anchors.top: parent.top
visible: root.downloadable
enabled: !downloadButton.downloadUnavailable
globalHover: (mouseArea.containsMouse || projectNameMouseArea.containsMouse)
&& !downloadButton.alreadyDownloaded
&& !downloadButton.downloadUnavailable
&& !downloadButton.updateAvailable
alreadyDownloaded: extractor.targetFolderExists
downloadUnavailable: !downloader.available
updateAvailable: downloader.lastModified > extractor.birthTime
FileDownloader {
id: downloader
downloadEnabled: $dataModel.downloadEnabled()
probeUrl: true
onFinishedChanged: {
downloadPanel.text = downloadPanel.extractText
downloadPanel.allowCancel = false
downloadPanel.value = Qt.binding(function() { return extractor.progress })
extractor.extract()
}
onDownloadStarting: {
$dataModel.usageStatisticsDownloadExample(downloader.name)
}
onDownloadCanceled: {
downloadPanel.text = ""
downloadPanel.value = 0
twirlToMainAnimation.start()
mouseArea.enabled = true
}
}
FileExtractor {
id: extractor
archiveName: downloader.completeBaseName
sourceFile: downloader.outputFile
targetPath: $dataModel.targetPath()
alwaysCreateDir: true
clearTargetPathContents: true
onFinishedChanged: {
twirlToMainAnimation.start()
root.bannerLabelText = qsTr("Recently Downloaded")
mouseArea.enabled = true
}
}
Connections {
target: downloadButton
onDownloadClicked: {
if (downloadButton.alreadyDownloaded) {
overwriteDialog.open()
} else {
if (downloadButton.enabled)
root.startDownload()
}
}
}
Connections {
target: $dataModel
onTargetPathMustChange: (path) => {
extractor.changeTargetPath(path)
}
}
}
}
Text {
id: projectPathLabel
visible: root.hasPath
color: Constants.currentBrand
text: typeof(prettyFilePath) === "undefined" ? "" : prettyFilePath
elide: Text.ElideLeft
renderType: Text.NativeRendering
font.pixelSize: 16
anchors.left: parent.left
anchors.right: parent.right
anchors.top: projectNameBackground.bottom
anchors.topMargin: 5
anchors.rightMargin: Constants.thumbnailMargin
anchors.leftMargin: Constants.thumbnailMargin
leftPadding: 5
MouseArea {
id: projectPathMouseArea
anchors.fill: parent
hoverEnabled: true
// Only enable the MouseArea if label actually contains text
enabled: projectPathLabel.text !== ""
}
ToolTip {
id: projectPathToolTip
y: -projectPathToolTip.height
visible: projectPathMouseArea.containsMouse && projectPathLabel.truncated
text: projectPathLabel.text
delay: 1000
height: 20
background: Rectangle {
color: Constants.currentToolTipBackground
border.color: Constants.currentToolTipOutline
border.width: 1
}
contentItem: Text {
color: Constants.currentToolTipText
text: projectPathToolTip.text
verticalAlignment: Text.AlignVCenter
}
}
}
Rectangle {
id: updateBackground
width: 200
height: 25
visible: root.bannerLabelText !== ""
color: Constants.currentBrand
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.topMargin: 0
anchors.rightMargin: Constants.thumbnailMargin
anchors.leftMargin: Constants.thumbnailMargin
Text {
id: updateText
color: Constants.darkActiveGlobalText
text: typeof(displayName) === "bannerText" ? "" : bannerText
anchors.fill: parent
font.pixelSize: 12
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
}
}
Rectangle {
id: detailsPanel
height: Constants.thumbnailSize
anchors.left: parent.left
anchors.right: parent.right
anchors.top: mainPanel.bottom
color: Constants.currentNormalThumbnailBackground
radius: 10
Text {
id: recentProjectInfo
color: Constants.currentGlobalText
text: typeof(description) === "undefined" ? "" : description
anchors.fill: parent
font.pixelSize: 12
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignTop
wrapMode: Text.WordWrap
anchors.margins: Constants.thumbnailMargin
anchors.topMargin: 25
}
TagArea {
id: tagArea
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.bottomMargin: Constants.thumbnailMargin
anchors.rightMargin: Constants.thumbnailMargin
anchors.leftMargin: Constants.thumbnailMargin
tags: typeof(tagData) === "undefined" ? "" : tagData.split(",")
}
}
}
states: [
State {
name: "normal"
when: !mouseArea.containsMouse && !(mouseArea.pressedButtons & Qt.LeftButton)
&& !projectPathMouseArea.containsMouse && !projectNameMouseArea.containsMouse
&& !downloadButton.isHovered && !twirlButtonDown.isHovered
&& !twirlButtonUp.isHovered
PropertyChanges {
target: mainPanel
color: Constants.currentNormalThumbnailBackground
border.width: 0
}
PropertyChanges {
target: detailsPanel
color: Constants.currentNormalThumbnailBackground
border.width: 0
}
PropertyChanges {
target: projectNameBackground
color: Constants.currentNormalThumbnailLabelBackground
}
PropertyChanges {
target: twirlButtonDown
parentHovered: false
}
PropertyChanges {
target: twirlButtonUp
parentHovered: false
}
},
State {
name: "hover"
when: (mouseArea.containsMouse || projectPathMouseArea.containsMouse
|| projectNameMouseArea.containsMouse || downloadButton.isHovered
|| twirlButtonDown.isHovered || twirlButtonUp.isHovered)
&& !(mouseArea.pressedButtons & Qt.LeftButton) && !root.hasDescription
PropertyChanges {
target: mainPanel
color: Constants.currentHoverThumbnailBackground
border.width: 0
}
PropertyChanges {
target: detailsPanel
color: Constants.currentHoverThumbnailBackground
border.width: 0
}
PropertyChanges {
target: projectNameBackground
color: Constants.currentHoverThumbnailLabelBackground
}
PropertyChanges {
target: thumbnailPlaceholder
visible: true
}
PropertyChanges {
target: twirlButtonDown
parentHovered: true
}
PropertyChanges {
target: twirlButtonUp
parentHovered: true
}
},
State {
name: "press"
when: (mouseArea.pressedButtons & Qt.LeftButton) && !root.hasDescription
PropertyChanges {
target: mainPanel
color: Constants.currentHoverThumbnailBackground
border.color: Constants.currentBrand
border.width: 2
}
PropertyChanges {
target: detailsPanel
color: Constants.currentHoverThumbnailBackground
border.color: Constants.currentBrand
border.width: 2
}
PropertyChanges {
target: projectNameBackground
color: Constants.currentBrand
}
PropertyChanges {
target: thumbnailPlaceholder
visible: true
}
PropertyChanges {
target: projectNameLabel
color: Constants.darkActiveGlobalText
}
},
State {
name: "hoverDescription"
when: mouseArea.containsMouse && !(mouseArea.pressedButtons & Qt.LeftButton)
&& root.hasDescription
PropertyChanges {
target: mainPanel
color: Constants.currentHoverThumbnailBackground
border.width: 0
}
PropertyChanges {
target: projectNameBackground
color: Constants.currentHoverThumbnailLabelBackground
}
PropertyChanges {
target: thumbnailPlaceholder
visible: true
}
},
State {
name: "pressDescription"
when: (mouseArea.pressedButtons & Qt.LeftButton) && root.hasDescription
PropertyChanges {
target: mainPanel
color: Constants.currentHoverThumbnailBackground
border.color: Constants.currentBrand
border.width: 2
}
PropertyChanges {
target: projectNameBackground
color: Constants.currentBrand
}
PropertyChanges {
target: thumbnailPlaceholder
visible: true
}
}
]
TwirlButton {
id: twirlButtonDown
height: 20
visible: root.currentPanel === ThumbnailDelegate.Panel.Main
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
Connections {
target: twirlButtonDown
onTriggerRelease: twirlToDetailsAnimation.start()
}
}
TwirlButton {
id: twirlButtonUp
height: 20
visible: root.currentPanel === ThumbnailDelegate.Panel.Details
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
rotation: 180
Connections {
target: twirlButtonUp
onTriggerRelease: twirlToMainAnimation.start()
}
}
NumberAnimation {
id: twirlToDetailsAnimation
target: canvas
property: "y"
easing.bezierCurve: [0.972,-0.176,0.0271,1.16,1,1]
duration: 250
alwaysRunToEnd: true
to: -Constants.thumbnailSize // dynamic size because of rescale - needs to be inverted because animation goes into negative range
from: canvas.y
}
NumberAnimation {
id: twirlToMainAnimation
target: canvas
property: "y"
easing.bezierCurve: [0.972,-0.176,0.0271,1.16,1,1]
alwaysRunToEnd: true
duration: 250
to: 0
from: canvas.y
}
NumberAnimation {
id: twirlToDownloadAnimation
target: canvas
property: "y"
easing.bezierCurve: [0.972,-0.176,0.0271,1.16,1,1]
alwaysRunToEnd: true
duration: 250
to: Constants.thumbnailSize
from: canvas.y
}
Connections {
target: twirlToDetailsAnimation
onStarted: root.currentPanel = ThumbnailDelegate.Panel.InBetween
onFinished: {
root.currentPanel = ThumbnailDelegate.Panel.Details
canvas.y = Qt.binding(function() {return -Constants.thumbnailSize })
}
}
Connections {
target: twirlToMainAnimation
onStarted: root.currentPanel = ThumbnailDelegate.Panel.InBetween
onFinished: {
root.currentPanel = ThumbnailDelegate.Panel.Main
canvas.y = Qt.binding(function() {return 0 })
}
}
Connections {
target: twirlToDownloadAnimation
onStarted: root.currentPanel = ThumbnailDelegate.Panel.InBetween
onFinished: {
root.currentPanel = ThumbnailDelegate.Panel.Download
canvas.y = Qt.binding(function() {return Constants.thumbnailSize })
}
}
}