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 }