Warning, /frameworks/kirigami/src/controls/AboutItem.qml is written in an unsupported language. File is not indexed.

0001 /*
0002  *  SPDX-FileCopyrightText: 2018 Aleix Pol Gonzalez <aleixpol@blue-systems.com>
0003  *  SPDX-FileCopyrightText: 2023 ivan tkachenko <me@ratijas.tk>
0004  *
0005  *  SPDX-License-Identifier: LGPL-2.0-or-later
0006  */
0007 
0008 import QtQuick
0009 import QtQuick.Controls as QQC2
0010 import QtQuick.Layouts
0011 import org.kde.kirigami as Kirigami
0012 
0013 //TODO: Kf6: move somewhere else which can depend from KAboutData?
0014 /**
0015  * @brief An about item that displays the about data
0016  *
0017  * Allows to show the copyright notice of the application
0018  * together with the contributors and some information of which platform it's
0019  * running on.
0020  *
0021  * @since 5.87
0022  * @since org.kde.kirigami 2.19
0023  */
0024 Item {
0025     id: aboutItem
0026     /**
0027      * @brief This property holds an object with the same shape as KAboutData.
0028      *
0029      * Example usage:
0030      * @code{json}
0031      * aboutData: {
0032           "displayName" : "KirigamiApp",
0033           "productName" : "kirigami/app",
0034           "componentName" : "kirigamiapp",
0035           "shortDescription" : "A Kirigami example",
0036           "homepage" : "",
0037           "bugAddress" : "submit@bugs.kde.org",
0038           "version" : "5.14.80",
0039           "otherText" : "",
0040           "authors" : [
0041               {
0042                   "name" : "...",
0043                   "task" : "",
0044                   "emailAddress" : "somebody@kde.org",
0045                   "webAddress" : "",
0046                   "ocsUsername" : ""
0047               }
0048           ],
0049           "credits" : [],
0050           "translators" : [],
0051           "licenses" : [
0052               {
0053                   "name" : "GPL v2",
0054                   "text" : "long, boring, license text",
0055                   "spdx" : "GPL-2.0"
0056               }
0057           ],
0058           "copyrightStatement" : "© 2010-2018 Plasma Development Team",
0059           "desktopFileName" : "org.kde.kirigamiapp"
0060        }
0061        @endcode
0062      *
0063      * @see KAboutData
0064      */
0065     property var aboutData
0066 
0067     /**
0068      * @brief This property holds a link to a "Get Involved" page.
0069      *
0070      * default: `"https://community.kde.org/Get_Involved" when application id starts with "org.kde.", otherwise it is empty.`
0071      */
0072     property url getInvolvedUrl: aboutData.desktopFileName.startsWith("org.kde.") ? "https://community.kde.org/Get_Involved" : ""
0073 
0074     /**
0075      * @brief This property holds a link to a "Donate" page.
0076      *
0077      * default: `"https://kde.org/community/donations" when application id starts with "org.kde.", otherwise it is empty.`
0078      */
0079     property url donateUrl: aboutData.desktopFileName.startsWith("org.kde.") ? "https://kde.org/community/donations" : ""
0080 
0081     /** @internal */
0082     property bool _usePageStack: false
0083 
0084     /**
0085      * @see org::kde::kirigami::FormLayout::wideMode
0086      * @property bool wideMode
0087      */
0088     property alias wideMode: form.wideMode
0089 
0090     /** @internal */
0091     default property alias _content: form.data
0092 
0093     // if aboutData is a native KAboutData object, avatarUrl should be a proper url instance,
0094     // otherwise if it was defined as a string in pure JavaScript it should work too.
0095     readonly property bool __hasAvatars: aboutItem.aboutData.authors.some(__hasAvatar)
0096 
0097     function __hasAvatar(person): bool {
0098         return typeof person.avatarUrl !== "undefined"
0099             && person.avatarUrl.toString().length > 0;
0100     }
0101 
0102     /**
0103      * @brief This property controls whether to load avatars by URL.
0104      *
0105      * If set to false, a fallback "user" icon will be displayed.
0106      *
0107      * default: ``false``
0108      */
0109     property bool loadAvatars: false
0110 
0111     implicitHeight: form.implicitHeight
0112     implicitWidth: form.implicitWidth
0113 
0114     Component {
0115         id: personDelegate
0116 
0117         RowLayout {
0118             id: delegate
0119 
0120             // type: KAboutPerson | { name?, task?, emailAddress?, webAddress?, avatarUrl? }
0121             required property var modelData
0122 
0123             property bool hasAvatar: aboutItem.__hasAvatar(modelData)
0124 
0125             Layout.fillWidth: true
0126 
0127             spacing: Kirigami.Units.smallSpacing * 2
0128 
0129             Kirigami.Icon {
0130                 id: avatarIcon
0131 
0132                 implicitWidth: Kirigami.Units.iconSizes.medium
0133                 implicitHeight: implicitWidth
0134 
0135                 fallback: "user"
0136                 source: {
0137                     if (delegate.hasAvatar && aboutItem.loadAvatars) {
0138                         // Appending to the params of the url does not work, thus the search is set
0139                         const url = new URL(modelData.avatarUrl);
0140                         const params = new URLSearchParams(url.search);
0141                         params.append("s", width);
0142                         url.search = params.toString();
0143                         return url;
0144                     } else {
0145                         return "user"
0146                     }
0147                 }
0148                 visible: status !== Kirigami.Icon.Loading
0149             }
0150 
0151             // So it's clear that something is happening while avatar images are loaded
0152             QQC2.BusyIndicator {
0153                 implicitWidth: Kirigami.Units.iconSizes.medium
0154                 implicitHeight: implicitWidth
0155 
0156                 visible: avatarIcon.status === Kirigami.Icon.Loading
0157                 running: visible
0158             }
0159 
0160             QQC2.Label {
0161                 Layout.fillWidth: true
0162                 readonly property bool withTask: typeof(modelData.task) !== "undefined" && modelData.task.length > 0
0163                 text: withTask ? qsTr("%1 (%2)").arg(modelData.name).arg(modelData.task) : modelData.name
0164                 wrapMode: Text.WordWrap
0165             }
0166 
0167             QQC2.ToolButton {
0168                 visible: typeof(modelData.emailAddress) !== "undefined" && modelData.emailAddress.length > 0
0169                 icon.name: "mail-sent"
0170                 QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
0171                 QQC2.ToolTip.visible: hovered
0172                 QQC2.ToolTip.text: qsTr("Send an email to %1").arg(modelData.emailAddress)
0173                 onClicked: Qt.openUrlExternally("mailto:%1".arg(modelData.emailAddress))
0174             }
0175 
0176             QQC2.ToolButton {
0177                 visible: typeof(modelData.webAddress) !== "undefined" && modelData.webAddress.length > 0
0178                 icon.name: "globe"
0179                 QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
0180                 QQC2.ToolTip.visible: hovered
0181                 QQC2.ToolTip.text: (typeof(modelData.webAddress) === "undefined" && modelData.webAddress.length > 0) ? "" : modelData.webAddress
0182                 onClicked: Qt.openUrlExternally(modelData.webAddress)
0183             }
0184         }
0185     }
0186 
0187     Kirigami.FormLayout {
0188         id: form
0189 
0190         anchors.fill: parent
0191 
0192         GridLayout {
0193             columns: 2
0194             Layout.fillWidth: true
0195 
0196             Kirigami.Icon {
0197                 Layout.rowSpan: 3
0198                 Layout.preferredHeight: Kirigami.Units.iconSizes.huge
0199                 Layout.preferredWidth: height
0200                 Layout.maximumWidth: aboutItem.width / 3;
0201                 Layout.rightMargin: Kirigami.Units.largeSpacing
0202                 source: Kirigami.Settings.applicationWindowIcon || aboutItem.aboutData.programLogo || aboutItem.aboutData.programIconName || aboutItem.aboutData.componentName
0203             }
0204 
0205             Kirigami.Heading {
0206                 Layout.fillWidth: true
0207                 text: aboutItem.aboutData.displayName + " " + aboutItem.aboutData.version
0208                 wrapMode: Text.WordWrap
0209             }
0210 
0211             Kirigami.Heading {
0212                 Layout.fillWidth: true
0213                 level: 2
0214                 wrapMode: Text.WordWrap
0215                 text: aboutItem.aboutData.shortDescription
0216             }
0217 
0218             RowLayout {
0219                 spacing: Kirigami.Units.largeSpacing * 2
0220 
0221                 UrlButton {
0222                     text: qsTr("Get Involved")
0223                     url: aboutItem.getInvolvedUrl
0224                     visible: url.toString().length > 0
0225                 }
0226 
0227                 UrlButton {
0228                     text: qsTr("Donate")
0229                     url: aboutItem.donateUrl
0230                     visible: url.toString().length > 0
0231                 }
0232 
0233                 UrlButton {
0234                     readonly property string theUrl: {
0235                         if (aboutItem.aboutData.bugAddress !== "submit@bugs.kde.org") {
0236                             return aboutItem.aboutData.bugAddress
0237                         }
0238                         const elements = aboutItem.aboutData.productName.split('/');
0239                         let url = `https://bugs.kde.org/enter_bug.cgi?format=guided&product=${elements[0]}&version=${aboutItem.aboutData.version}`;
0240                         if (elements.length === 2) {
0241                             url += "&component=" + elements[1];
0242                         }
0243                         return url;
0244                     }
0245                     text: qsTr("Report a Bug")
0246                     url: theUrl
0247                     visible: theUrl.toString().length > 0
0248                 }
0249             }
0250         }
0251 
0252         Separator {
0253             Layout.fillWidth: true
0254         }
0255 
0256         Kirigami.Heading {
0257             Kirigami.FormData.isSection: true
0258             text: qsTr("Copyright")
0259         }
0260 
0261         QQC2.Label {
0262             Layout.leftMargin: Kirigami.Units.gridUnit
0263             text: aboutItem.aboutData.otherText
0264             visible: text.length > 0
0265             wrapMode: Text.WordWrap
0266             Layout.fillWidth: true
0267         }
0268 
0269         QQC2.Label {
0270             Layout.leftMargin: Kirigami.Units.gridUnit
0271             text: aboutItem.aboutData.copyrightStatement
0272             visible: text.length > 0
0273             wrapMode: Text.WordWrap
0274             Layout.fillWidth: true
0275         }
0276 
0277         UrlButton {
0278             Layout.leftMargin: Kirigami.Units.gridUnit
0279             url: aboutItem.aboutData.homepage
0280             visible: url.length > 0
0281             wrapMode: Text.WordWrap
0282             Layout.fillWidth: true
0283         }
0284 
0285         OverlaySheet {
0286             id: licenseSheet
0287             property alias text: bodyLabel.text
0288 
0289             contentItem: SelectableLabel {
0290                 id: bodyLabel
0291                 text: licenseSheet.text
0292                 wrapMode: Text.Wrap
0293             }
0294         }
0295 
0296         Component {
0297             id: licenseLinkButton
0298 
0299             RowLayout {
0300                 Layout.leftMargin: Kirigami.Units.smallSpacing
0301 
0302                 QQC2.Label { text: qsTr("License:") }
0303 
0304                 LinkButton {
0305                     Layout.fillWidth: true
0306                     wrapMode: Text.WordWrap
0307                     text: modelData.name
0308                     onClicked: mouse => {
0309                         licenseSheet.text = modelData.text
0310                         licenseSheet.title = modelData.name
0311                         licenseSheet.open()
0312                     }
0313                 }
0314             }
0315         }
0316 
0317         Component {
0318             id: licenseTextItem
0319 
0320             QQC2.Label {
0321                 Layout.leftMargin: Kirigami.Units.smallSpacing
0322                 Layout.fillWidth: true
0323                 wrapMode: Text.WordWrap
0324                 text: qsTr("License: %1").arg(modelData.name)
0325             }
0326         }
0327 
0328         Repeater {
0329             model: aboutItem.aboutData.licenses
0330             delegate: _usePageStack ? licenseLinkButton : licenseTextItem
0331         }
0332 
0333         Kirigami.Heading {
0334             Kirigami.FormData.isSection: visible
0335             text: qsTr("Libraries in use")
0336             Layout.fillWidth: true
0337             wrapMode: Text.WordWrap
0338             visible: Kirigami.Settings.information
0339         }
0340 
0341         Repeater {
0342             model: Kirigami.Settings.information
0343             delegate: QQC2.Label {
0344                 Layout.leftMargin: Kirigami.Units.gridUnit
0345                 Layout.fillWidth: true
0346                 wrapMode: Text.WordWrap
0347                 id: libraries
0348                 text: modelData
0349             }
0350         }
0351 
0352         Repeater {
0353             model: aboutItem.aboutData.components
0354             delegate: QQC2.Label {
0355                 Layout.fillWidth: true
0356                 wrapMode: Text.WordWrap
0357                 Layout.leftMargin: Kirigami.Units.gridUnit
0358                 text: modelData.name + (modelData.version.length === 0 ? "" : " %1".arg(modelData.version))
0359             }
0360         }
0361 
0362         Kirigami.Heading {
0363             Layout.fillWidth: true
0364             Kirigami.FormData.isSection: visible
0365             text: qsTr("Authors")
0366             wrapMode: Text.WordWrap
0367             visible: aboutItem.aboutData.authors.length > 0
0368         }
0369 
0370         QQC2.CheckBox {
0371             id: remoteAvatars
0372             visible: aboutItem.__hasAvatars
0373             checked: aboutItem.loadAvatars
0374             onToggled: aboutItem.loadAvatars = checked
0375             text: qsTr("Show author photos")
0376         }
0377 
0378         Repeater {
0379             id: authorsRepeater
0380             model: aboutItem.aboutData.authors
0381             delegate: personDelegate
0382         }
0383 
0384         Kirigami.Heading {
0385             Kirigami.FormData.isSection: visible
0386             text: qsTr("Credits")
0387             visible: repCredits.count > 0
0388         }
0389 
0390         Repeater {
0391             id: repCredits
0392             model: aboutItem.aboutData.credits
0393             delegate: personDelegate
0394         }
0395 
0396         Kirigami.Heading {
0397             Kirigami.FormData.isSection: visible
0398             text: qsTr("Translators")
0399             visible: repTranslators.count > 0
0400         }
0401 
0402         Repeater {
0403             id: repTranslators
0404             model: aboutItem.aboutData.translators
0405             delegate: personDelegate
0406         }
0407     }
0408 }