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 }