Warning, /plasma/latte-dock/plasmoid/package/contents/ui/previews/ToolTipInstance.qml is written in an unsupported language. File is not indexed.

0001 /*
0002     SPDX-FileCopyrightText: 2013 Sebastian Kügler <sebas@kde.org>
0003     SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
0004     SPDX-FileCopyrightText: 2016 Kai Uwe Broulik <kde@privat.broulik.de>
0005     SPDX-FileCopyrightText: 2017 Roman Gilg <subdiff@gmail.com>
0006 
0007     SPDX-License-Identifier: LGPL-2.0-or-later
0008 */
0009 
0010 import QtQuick 2.6
0011 import QtQuick.Layouts 1.1
0012 import QtGraphicalEffects 1.0
0013 import QtQml.Models 2.2
0014 
0015 import org.kde.plasma.core 2.0 as PlasmaCore
0016 import org.kde.plasma.components 2.0 as PlasmaComponents
0017 import org.kde.plasma.extras 2.0 as PlasmaExtras
0018 import org.kde.kquickcontrolsaddons 2.0 as KQuickControlsAddons
0019 
0020 import org.kde.latte.core 0.2 as LatteCore
0021 
0022 import org.kde.draganddrop 2.0
0023 
0024 import org.kde.taskmanager 0.1 as TaskManager
0025 
0026 Column {
0027     id: instance
0028     property var submodelIndex
0029     property int flatIndex: isGroup && itemIndex>=0 ? itemIndex : 0
0030 
0031     property bool isActive: (typeof model !== 'undefined') && (typeof model.IsActive !== 'undefined') ? IsActive : false
0032     property bool isMinimized: (typeof model !== 'undefined') && (typeof model.IsMinimized !== 'undefined') ? IsMinimized : false
0033 
0034     property int appPid: (typeof model !== 'undefined') && (typeof model.AppPid !== 'undefined') ? AppPid : -1
0035     property int itemIndex: (typeof model !== 'undefined') && (typeof model.index !== 'undefined') ? index : 0
0036     property int virtualDesktop: (typeof model !== 'undefined') && (typeof model.VirtualDesktop !== 'undefined') ? VirtualDesktop : 0
0037     property var activities : (typeof model !== 'undefined') && (typeof model.Activities !== 'undefined') ? Activities : []
0038 
0039     spacing: units.smallSpacing
0040 
0041     readonly property bool descriptionIsVisible: winDescription.text !== ""
0042 
0043     readonly property string mprisSourceName: mpris2Source.sourceNameForLauncherUrl(toolTipDelegate.launcherUrl, isGroup ? appPid : pidParent)
0044     readonly property var playerData: mprisSourceName != "" ? mpris2Source.data[mprisSourceName] : 0
0045     readonly property bool hasPlayer: !!mprisSourceName && !!playerData
0046     readonly property bool playing: hasPlayer && playerData.PlaybackStatus === "Playing"
0047     readonly property bool canControl: hasPlayer && playerData.CanControl
0048     readonly property bool canPlay: hasPlayer && playerData.CanPlay
0049     readonly property bool canPause: hasPlayer && playerData.CanPause
0050     readonly property bool canGoBack: hasPlayer && playerData.CanGoPrevious
0051     readonly property bool canGoNext: hasPlayer && playerData.CanGoNext
0052     readonly property bool canRaise: hasPlayer && playerData.CanRaise
0053     readonly property var currentMetadata: hasPlayer ? playerData.Metadata : ({})
0054 
0055     readonly property string track: {
0056         var xesamTitle = currentMetadata["xesam:title"]
0057         if (xesamTitle) {
0058             return xesamTitle;
0059         }
0060         // if no track title is given, print out the file name
0061         var xesamUrl = currentMetadata["xesam:url"] ? currentMetadata["xesam:url"].toString() : ""
0062         if (!xesamUrl) {
0063             return "";
0064         }
0065         var lastSlashPos = xesamUrl.lastIndexOf('/')
0066         if (lastSlashPos < 0) {
0067             return "";
0068         }
0069         var lastUrlPart = xesamUrl.substring(lastSlashPos + 1)
0070         return decodeURIComponent(lastUrlPart);
0071     }
0072     readonly property string artist: currentMetadata["xesam:artist"] || ""
0073     readonly property string albumArt: currentMetadata["mpris:artUrl"] || ""
0074 
0075     //
0076     function isTaskActive() {
0077         return (isGroup ? isActive : (parentTask ? parentTask.isActive : false));
0078     }
0079 
0080     // launcher icon + text labels + close button
0081     RowLayout {
0082         id: header
0083         Layout.minimumWidth: childrenRect.width
0084         Layout.maximumWidth: Layout.minimumWidth
0085 
0086         Layout.minimumHeight: childrenRect.height
0087         Layout.maximumHeight: Layout.minimumHeight
0088 
0089         anchors.horizontalCenter: parent.horizontalCenter
0090 
0091         // launcher icon
0092         PlasmaCore.IconItem {
0093             Layout.preferredWidth: units.iconSizes.medium
0094             Layout.preferredHeight: units.iconSizes.medium
0095             source: icon
0096             animated: false
0097             usesPlasmaTheme: false
0098             visible: !isWin
0099         }
0100         // all textlabels
0101         Column {
0102             PlasmaExtras.Heading {
0103                 level: 3
0104                 width: isWin ? textWidth : undefined
0105                 height: undefined
0106                 maximumLineCount: 1
0107                 elide: Text.ElideRight
0108                 text: appName
0109                 opacity: flatIndex == 0
0110                 textFormat: Text.PlainText
0111                 visible: text !== ""
0112             }
0113             // window title
0114             PlasmaExtras.Heading {
0115                 id: winTitle
0116                 level: 5
0117                 width: isWin ? textWidth : undefined
0118                 height: undefined
0119                 maximumLineCount: 1
0120                 elide: Text.ElideRight
0121                 text: generateTitle()
0122                 textFormat: Text.PlainText
0123                 opacity: 0.75
0124                 visible: !hasPlayer
0125             }
0126             // subtext
0127             PlasmaExtras.Heading {
0128                 id: winDescription
0129                 level: 6
0130                 width: isWin ? textWidth : undefined
0131                 height: undefined
0132                 maximumLineCount: 1
0133                 elide: Text.ElideRight
0134                 text: isWin ? generateSubText() : ""
0135                 textFormat: Text.PlainText
0136                 opacity: 0.6
0137                 visible: text !== "" || instance.parent.hasVisibleDescription
0138             }
0139         }
0140         // close button
0141         PlasmaComponents.ToolButton {
0142             //! It creates issues with Valgrind and needs to be completely removed in that case
0143             id: closeButton
0144             Layout.alignment: Qt.AlignRight | Qt.AlignTop
0145             visible: isWin && !hideCloseButtons
0146             iconSource: "window-close"
0147             onClicked: {
0148                 if (!isGroup) {
0149                     //! force windowsPreviewDlg hiding when the last instance is closed
0150                     windowsPreviewDlg.visible = false;
0151                 }
0152 
0153                 backend.cancelHighlightWindows();
0154                 tasksModel.requestClose(submodelIndex);
0155             }
0156         }
0157     }
0158 
0159     // thumbnail container
0160     Item {
0161         id: thumbnail
0162         anchors.horizontalCenter: parent.horizontalCenter
0163 
0164         width: header.width
0165         // similar to 0.5625 = 1 / (16:9) as most screens are
0166         // round necessary, otherwise shadow mask for players has gap!
0167         height: Math.round(screenGeometryHeightRatio * width) + (!winTitle.visible? Math.round(winTitle.height) : 0) + activeTaskLine.height
0168 
0169         visible: isWin
0170 
0171         readonly property real screenGeometryHeightRatio: appletAbilities.myView.screenGeometry.height / appletAbilities.myView.screenGeometry.width
0172 
0173         Item {
0174             id: thumbnailSourceItem
0175             anchors.fill: parent
0176             anchors.bottomMargin: 2
0177 
0178             readonly property bool isMinimized: isGroup ? instance.isMinimized : mainToolTip.isMinimizedParent
0179             // TODO: this causes XCB error message when being visible the first time
0180             readonly property var winId: isWin && windows[flatIndex] !== undefined ? windows[flatIndex] : 0
0181 
0182             // There's no PlasmaComponents3 version
0183             PlasmaComponents.Highlight {
0184                 anchors.fill: hoverHandler
0185                 visible: hoverHandler.containsMouse
0186                 pressed: hoverHandler.containsPress
0187             }
0188 
0189             Loader{
0190                 id:previewThumbLoader
0191                 anchors.fill: parent
0192                 anchors.margins: Math.max(2, previewShadow.radius)
0193                 active: LatteCore.WindowSystem.isPlatformX11 || (root.plasma520 && LatteCore.WindowSystem.isPlatformWayland)
0194                 visible: !albumArtImage.visible && !thumbnailSourceItem.isMinimized
0195                 source:  {
0196                     if (LatteCore.WindowSystem.isPlatformWayland) {
0197                         if (root.plasmaAtLeast526) {
0198                             return "PipeWireThumbnail.5.26.qml";
0199                         } else if (root.plasmaAtLeast525) {
0200                             return "PipeWireThumbnail.5.25.qml";
0201                         } else if (root.plasmaAtLeast524) {
0202                             return "PipeWireThumbnail.5.24.qml";
0203                         }
0204                     }
0205 
0206                     return "PlasmaCoreThumbnail.qml";
0207                 }
0208 
0209                 DropShadow {
0210                     id: previewShadow
0211                     anchors.fill:  previewThumbLoader.item
0212                     visible: previewThumbLoader.item.visible
0213                     horizontalOffset: 0
0214                     verticalOffset: Math.round(3 * PlasmaCore.Units.devicePixelRatio)
0215                     radius: Math.round(8.0 * PlasmaCore.Units.devicePixelRatio)
0216                     samples: Math.round(radius * 1.5)
0217                     color: "Black"
0218                     source: previewThumbLoader.item
0219                 }
0220             }
0221 
0222             ToolTipWindowMouseArea {
0223                 id: hoverHandler
0224 
0225                 anchors.fill: parent
0226                 rootTask: parentTask
0227                 modelIndex: submodelIndex
0228                 winId: thumbnailSourceItem.winId
0229             }
0230 
0231             Image {
0232                 id: albumArtBackground
0233                 source: albumArt
0234                 anchors.fill: parent
0235                 fillMode: Image.PreserveAspectCrop
0236                 visible: albumArtImage.available
0237                 layer.enabled: true
0238                 opacity: 0.25
0239                 layer.effect: FastBlur {
0240                     source: albumArtBackground
0241                     anchors.fill: parent
0242                     radius: 30
0243                 }
0244             }
0245 
0246             Image {
0247                 id: albumArtImage
0248                 // also Image.Loading to prevent loading thumbnails just because the album art takes a split second to load
0249                 readonly property bool available: status === Image.Ready || status === Image.Loading
0250 
0251                 height: thumbnail.height - playbackLoader.realHeight
0252                 anchors.horizontalCenter: parent.horizontalCenter
0253                 sourceSize: Qt.size(parent.width, parent.height)
0254                 asynchronous: true
0255                 source: albumArt
0256                 fillMode: Image.PreserveAspectFit
0257                 visible: available
0258             }
0259 
0260             // when minimized, we don't have a preview, so show the icon
0261             PlasmaCore.IconItem {
0262                 width: parent.width
0263                 height: thumbnail.height - playbackLoader.realHeight
0264                 anchors.horizontalCenter: parent.horizontalCenter
0265                 source: icon
0266                 animated: false
0267                 usesPlasmaTheme: false
0268                 visible: (thumbnailSourceItem.isMinimized && !albumArtImage.visible) //X11 case
0269                          || (!previewThumbLoader.active && !albumArtImage.visible) //Wayland case
0270             }
0271         }
0272 
0273 
0274         Loader {
0275             id: playbackLoader
0276 
0277             property real realHeight: item? item.realHeight : 0
0278 
0279             anchors.fill: thumbnail
0280             active: hasPlayer
0281             sourceComponent: playerControlsComp
0282         }
0283 
0284         Component {
0285             id: playerControlsComp
0286 
0287             Item {
0288                 property real realHeight: playerControlsRow.height
0289 
0290                 anchors.fill: parent
0291 
0292                 // TODO: When could this really be the case? A not-launcher-task always has a window!?
0293                 // if there's no window associated with this task, we might still be able to raise the player
0294                 //                MouseArea {
0295                 //                    id: raisePlayerArea
0296                 //                    anchors.fill: parent
0297 
0298                 //                    visible: !isWin || !windows[0] && canRaise
0299                 //                    onClicked: mpris2Source.raise(mprisSourceName)
0300                 //                }
0301 
0302                 Item {
0303                     id: playerControlsFrostedGlass
0304                     anchors.fill: parent
0305                     visible: false // OpacityMask would render it
0306 
0307                     Rectangle {
0308                         width: parent.width
0309                         height: parent.height - playerControlsRow.height
0310                         opacity: 0
0311                     }
0312 
0313                     Rectangle {
0314                         anchors.bottom: parent.bottom
0315                         width: parent.width
0316                         height: playerControlsRow.height
0317                         color: theme.backgroundColor
0318                         opacity: 0.8
0319                     }
0320                 }
0321 
0322                 OpacityMask {
0323                     id: playerControlsOpacityMask
0324                     anchors.fill: parent
0325                     source: playerControlsFrostedGlass
0326                     maskSource: thumbnailSourceItem
0327                 }
0328 
0329                 // prevent accidental click-through when a control is disabled
0330                 MouseArea {
0331                     id: area3
0332                     anchors.fill: playerControlsRow
0333                 }
0334 
0335                 RowLayout {
0336                     id: playerControlsRow
0337                     anchors {
0338                         horizontalCenter: parent.horizontalCenter
0339                         bottom: parent.bottom
0340                     }
0341                     width: parent.width
0342                     spacing: 0
0343                     enabled: canControl
0344 
0345                     ColumnLayout {
0346                         Layout.margins: 2
0347                         Layout.fillWidth: true
0348                         spacing: 0
0349 
0350                         PlasmaComponents.Label {
0351                             Layout.fillWidth: true
0352                             lineHeight: 1
0353                             maximumLineCount: artistText.visible? 1 : 2
0354                             wrapMode: artistText.visible? Text.NoWrap : Text.Wrap
0355                             elide: Text.ElideRight
0356                             text: track || ""
0357                         }
0358 
0359                          PlasmaExtras.DescriptiveLabel {
0360                             id: artistText
0361                             Layout.fillWidth: true
0362                             wrapMode: Text.NoWrap
0363                             lineHeight: 1
0364                             elide: Text.ElideRight
0365                             text: artist || ""
0366                             visible: text != ""
0367                             font.pointSize: theme.smallestFont.pointSize
0368                         }
0369                     }
0370 
0371                    PlasmaComponents.ToolButton {
0372                        //! It creates issues with Valgrind and needs to be completely removed in that case
0373                        id: canGoBackButton
0374                        enabled: canGoBack
0375                        iconSource: LayoutMirroring.enabled ? "media-skip-forward" : "media-skip-backward"
0376                        onClicked: mpris2Source.goPrevious(mprisSourceName)
0377                    }
0378 
0379                    PlasmaComponents.ToolButton {
0380                        //! It creates issues with Valgrind and needs to be completely removed in that case
0381                        id: playingButton
0382                        enabled: playing ? canPause : canPlay
0383                        iconSource: playing ? "media-playback-pause" : "media-playback-start"
0384                        onClicked: {
0385                            if (!playing) {
0386                                mpris2Source.play(mprisSourceName);
0387                            } else {
0388                                mpris2Source.pause(mprisSourceName);
0389                            }
0390                        }
0391                    }
0392 
0393                    PlasmaComponents.ToolButton {
0394                        //! It creates issues with Valgrind and needs to be completely removed in that case
0395                        id: canGoNextButton
0396                        enabled: canGoNext
0397                        iconSource: LayoutMirroring.enabled ? "media-skip-backward" : "media-skip-forward"
0398                        onClicked: mpris2Source.goNext(mprisSourceName)
0399                    }
0400 
0401                 }
0402             }
0403         }
0404 
0405         Rectangle{
0406             id: activeTaskLine
0407             anchors.bottom: parent.bottom
0408             width: header.width
0409             height: 3
0410             opacity: isTaskActive() ? 1 : 0
0411             color: theme.buttonFocusColor
0412         }
0413     }
0414 
0415     function generateTitle() {
0416         if (!isWin) {
0417             return genericName != undefined ? genericName : "";
0418         }
0419 
0420         var text;
0421         var modelExists = (typeof model !== 'undefined');
0422 
0423         if (isGroup && modelExists) {
0424             if (model.display === undefined) {
0425                 return "";
0426             }
0427             text = model.display.toString();
0428         } else {
0429             text = displayParent;
0430         }
0431 
0432         // KWin appends increasing integers in between pointy brackets to otherwise equal window titles.
0433         // In this case save <#number> as counter and delete it at the end of text.
0434         var counter = text.match(/<\d+>\W*$/);
0435         text = text.replace(/\s*<\d+>\W*$/, "");
0436 
0437         // Remove appName from the end of text.
0438         var appNameRegex = new RegExp(appName + "$", "i");
0439         text = text.replace(appNameRegex, "");
0440         text = text.replace(/\s*(?:-|—)*\s*$/, "");
0441 
0442         // Add counter back at the end.
0443         if (counter !== null) {
0444             if (text === "") {
0445                 text = counter;
0446             } else {
0447                 text = text + " " + counter;
0448             }
0449         }
0450 
0451         // In case the window title had only redundant information (i.e. appName), text is now empty.
0452         // Add a hyphen to indicate that and avoid empty space.
0453         if (text === "") {
0454             text = "—";
0455         }
0456         return text.toString();
0457     }
0458 
0459     function generateSubText() {
0460         if (activitiesParent === undefined) {
0461             return "";
0462         }
0463 
0464         var subTextEntries = [];
0465 
0466         var virtualDesktops = isGroup ? VirtualDesktops : virtualDesktopParent;
0467         var virtualDesktopNameList = [];
0468 
0469         for (var i = 0; i < virtualDesktops.length; ++i) {
0470             virtualDesktopNameList.push(virtualDesktopInfo.desktopNames[virtualDesktops[i] - 1]);
0471         }
0472 
0473         if (!root.showOnlyCurrentDesktop
0474             && virtualDesktopInfo.numberOfDesktops > 1
0475             && (isGroup ? IsOnAllVirtualDesktops : isOnAllVirtualDesktopsParent) !== true
0476             && virtualDesktops.length > 0) {
0477             subTextEntries.push(i18nc("Comma-separated list of desktops", "On %1", virtualDesktopNameList.join()));
0478         }
0479 
0480         var act = isGroup ? activities : activitiesParent;
0481 
0482         if (act === undefined) {
0483             return subTextEntries.join("\n");
0484         }
0485 
0486         if (act.length === 0 && activityInfo.numberOfRunningActivities > 1) {
0487             subTextEntries.push(i18nc("Which virtual desktop a window is currently on",
0488                                       "Available on all activities"));
0489         } else if (act.length > 0) {
0490             var activityNames = [];
0491 
0492             for (var i = 0; i < act.length; i++) {
0493                 var activity = act[i];
0494                 var activityName = activityInfo.activityName(act[i]);
0495                 if (activityName === "") {
0496                     continue;
0497                 }
0498                 if (root.showOnlyCurrentActivity) {
0499                     if (activity !== activityInfo.currentActivity) {
0500                         activityNames.push(activityName);
0501                     }
0502                 } else if (activity !== activityInfo.currentActivity) {
0503                     activityNames.push(activityName);
0504                 }
0505             }
0506 
0507             if (root.showOnlyCurrentActivity) {
0508                 if (activityNames.length > 0) {
0509                     subTextEntries.push(i18nc("Activities a window is currently on (apart from the current one)",
0510                                               "Also available on %1", activityNames.join(", ")));
0511                 }
0512             } else if (activityNames.length > 0) {
0513                 subTextEntries.push(i18nc("Which activities a window is currently on",
0514                                           "Available on %1", activityNames.join(", ")));
0515             }
0516         }
0517 
0518         return subTextEntries.join("\n");
0519     }
0520 }