aerothemeplasma/Plasma/Plasma_Widgets/User/io.gitgud.wackyideas.seventasks/contents/ui/ToolTipInstance.qml
2023-09-19 22:32:59 +02:00

577 lines
22 KiB
QML
Executable file

/*
SPDX-FileCopyrightText: 2013 Sebastian Kügler <sebas@kde.org>
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
SPDX-FileCopyrightText: 2016 Kai Uwe Broulik <kde@privat.broulik.de>
SPDX-FileCopyrightText: 2017 Roman Gilg <subdiff@gmail.com>
SPDX-FileCopyrightText: 2020 Nate Graham <nate@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
import QtQuick 2.6
import QtQuick.Layouts 1.1
import QtGraphicalEffects 1.0
import QtQml.Models 2.2
import org.kde.plasma.core 2.0 as PlasmaCore
// for Highlight
import org.kde.plasma.components 2.0 as PlasmaComponents
import org.kde.plasma.components 3.0 as PlasmaComponents3
import org.kde.plasma.extras 2.0 as PlasmaExtras
import org.kde.kquickcontrolsaddons 2.0 as KQuickControlsAddons
ColumnLayout {
id: tooltipInstance
property var submodelIndex
property int flatIndex: isGroup && index != undefined ? index : 0
readonly property string mprisSourceName: mpris2Source.sourceNameForLauncherUrl(toolTipDelegate.launcherUrl, isGroup ? AppPid : pidParent)
readonly property var playerData: mprisSourceName != "" ? mpris2Source.data[mprisSourceName] : 0
readonly property bool hasPlayer: !!mprisSourceName && !!playerData
readonly property bool playing: hasPlayer && playerData.PlaybackStatus === "Playing"
readonly property bool canControl: hasPlayer && playerData.CanControl
readonly property bool canPlay: hasPlayer && playerData.CanPlay
readonly property bool canPause: hasPlayer && playerData.CanPause
readonly property bool canGoBack: hasPlayer && playerData.CanGoPrevious
readonly property bool canGoNext: hasPlayer && playerData.CanGoNext
readonly property bool canRaise: hasPlayer && playerData.CanRaise
readonly property var currentMetadata: hasPlayer ? playerData.Metadata : ({})
// Used to highlight the tooltip that points to the window that's currently in focus.
property bool activeWindow: plasmoid.nativeInterface.isActiveWindow(thumbnailSourceItem.winId);
readonly property string track: {
var xesamTitle = currentMetadata["xesam:title"]
if (xesamTitle) {
return xesamTitle;
}
// if no track title is given, print out the file name
var xesamUrl = currentMetadata["xesam:url"] ? currentMetadata["xesam:url"].toString() : ""
if (!xesamUrl) {
return "";
}
var lastSlashPos = xesamUrl.lastIndexOf('/')
if (lastSlashPos < 0) {
return "";
}
var lastUrlPart = xesamUrl.substring(lastSlashPos + 1)
return decodeURIComponent(lastUrlPart);
}
readonly property string artist: currentMetadata["xesam:artist"] || ""
readonly property string albumArt: currentMetadata["mpris:artUrl"] || ""
spacing: isWin ? PlasmaCore.Units.smallSpacing * 2 : 0
// text labels + close button
RowLayout {
id: header
// match spacing of DefaultToolTip.qml in plasma-framework
//spacing: isWin ? PlasmaCore.Units.smallSpacing : 0 //isWin ? PlasmaCore.Units.smallSpacing : PlasmaCore.Units.largeSpacing
spacing: 0
// This number controls the overall size of the window tooltips
Layout.maximumWidth: PlasmaCore.Units.gridUnit * 12
Layout.minimumWidth: isWin ? Layout.maximumWidth : 0
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
// match margins of DefaultToolTip.qml in plasma-framework
Layout.margins: isWin ? 0 : PlasmaCore.Units.smallSpacing / 2
// There's no PlasmaComponents3 version
// This component tracks the mouse and highlights the tooltip when it's hovered over or clicked on.
ToolTipWindowMouseArea {
id: hoverHandler
Layout.alignment: Qt.AlignTop
height: {
if(playerControls.visible)
return tooltipInstance.height - parent.height;
else
return tooltipInstance.height;
}
width: header.width
rootTask: parentTask
modelIndex: submodelIndex
winId: thumbnailSourceItem.winId
z: 0
enabled: visible
closeBtn: closeButtonMA
PlasmaCore.FrameSvgItem {
imagePath: Qt.resolvedUrl("svgs/taskbarhover.svg")
z: -1
anchors {
top: parent.top;
left: parent.left;
right: parent.right;
bottom: parent.bottom;
bottomMargin: PlasmaCore.Units.smallSpacing + ((playerControls.visible) ? -header.height : 0);
leftMargin: PlasmaCore.Units.smallSpacing
rightMargin: PlasmaCore.Units.smallSpacing
topMargin: PlasmaCore.Units.smallSpacing
}
// The currently active window has a blue tinted version of the same texture.
prefix: {
if(activeWindow) return "active";
return "normal";
}
Behavior on opacity {
NumberAnimation { duration: 150 }
}
opacity: isWin ? (hoverHandler.containsPress ? 1.0 : ( (hoverHandler.opacityHover || closeButton.hovered) ? ((activeWindow) ? 1.0 : 0.7) : (activeWindow ? 0.7 : 0) )) : 0;
}
}
// The icon in the corner of the tooltip.
PlasmaCore.IconItem {
id: iconSmall
implicitWidth: PlasmaCore.Units.iconSizes.small;
implicitHeight: PlasmaCore.Units.iconSizes.small;
Layout.topMargin: PlasmaCore.Units.smallSpacing;
Layout.leftMargin: PlasmaCore.Units.smallSpacing*3;
Layout.alignment: Qt.AlignVCenter
source: icon
animated: false
usesPlasmaTheme: false
roundToIconSize: false
visible: valid && isWin
}
ColumnLayout {
spacing: 0
// Program name.
PlasmaExtras.Heading {
id: appNameHeading
level: 5
maximumLineCount: 1
lineHeight: isWin ? iconSmall.implicitHeight * 2 : 1.0
lineHeightMode: isWin ? Text.FixedHeight : Text.ProportionalHeight
Layout.fillWidth: true
Layout.topMargin: isWin ? 1 : 0;
verticalAlignment: Text.AlignVCenter
Layout.leftMargin: isWin ? PlasmaCore.Units.smallSpacing : 0;
Layout.rightMargin: (!isWin ? 0 : ((!closeButton.isvisible) ? (PlasmaCore.Units.smallSpacing*2) : PlasmaCore.Units.smallSpacing));
elide: Text.ElideRight
text: isWin ? generateTitle() : appName;
opacity: 1
textFormat: Text.PlainText
}
// Window title.
PlasmaComponents.Label {
id: winTitle
maximumLineCount: 1
lineHeight: 1.0
Layout.fillWidth: true
Layout.leftMargin: isWin ? PlasmaCore.Units.smallSpacing : 0;
Layout.rightMargin: (!isWin ? 0 : ((!closeButton.isvisible) ? (PlasmaCore.Units.smallSpacing*3) : 0));
elide: Text.ElideRight
text: "";
opacity: 0.75
visible: false
textFormat: Text.PlainText
}
// Subtext.
PlasmaComponents.Label {
readonly property string title: generateTitle()
id: subtext
maximumLineCount: 1
Layout.fillWidth: true
Layout.leftMargin: isWin ? PlasmaCore.Units.smallSpacing : 0;
Layout.rightMargin: (!isWin ? 0 : ((!closeButton.isvisible) ? (PlasmaCore.Units.smallSpacing*3) : 0));
elide: Text.ElideRight
text: title;
//text: isWin ? generateSubText() : ""
opacity: 0.7
visible: !isWin && (title.length !== 0)
textFormat: Text.PlainText
}
}
// Count badge, no longer used.
// The badge itself is inside an item to better center the text in the bubble
/*Item {
Layout.alignment: Qt.AlignRight | Qt.AlignTop
Layout.preferredHeight: closeButton.height
Layout.preferredWidth: closeButton.width
visible: flatIndex === 0 && smartLauncherCountVisible
Badge {
anchors.centerIn: parent
height: PlasmaCore.Units.iconSizes.smallMedium
number: smartLauncherCount
}
}*/
// Close button, which is no longer an icon but rather a SVG texture.
PlasmaCore.FrameSvgItem {
id: closeButton
property bool isvisible: closeButton.visible
Layout.alignment: Qt.AlignRight | Qt.AlignTop
Layout.preferredWidth: PlasmaCore.Units.smallSpacing*3+2;
Layout.preferredHeight: PlasmaCore.Units.smallSpacing*3+2;
Layout.rightMargin: PlasmaCore.Units.smallSpacing*3-1;
Layout.topMargin: PlasmaCore.Units.smallSpacing*3-1;
visible: isWin && hoverHandler.opacityHover
imagePath: Qt.resolvedUrl("svgs/button-close.svg")
prefix: closeButtonMA.containsPress ? "pressed" : (closeButtonMA.containsMouse ? "hover" : "normal");
MouseArea {
id: closeButtonMA
anchors.fill: parent
onClicked: {
backend.cancelHighlightWindows();
tasksModel.requestClose(submodelIndex);
}
onEntered: {
hoverHandler.opacityHover = 1;
tasks.windowsHovered([thumbnailSourceItem.winId], 1);
}
hoverEnabled: true
z: 7
}
z: 6
}
}
// Thumbnail container.
Item {
id: thumbnailSourceItem
Layout.minimumWidth: header.width - PlasmaCore.Units.smallSpacing*4;
Layout.preferredHeight: header.width / 2
Layout.leftMargin: PlasmaCore.Units.smallSpacing*2;
Layout.bottomMargin: albumArtImage.visible || hasPlayer ? 0 : PlasmaCore.Units.smallSpacing*2;
visible: toolTipDelegate.isWin
readonly property bool isMinimized: isGroup ? IsMinimized == true : isMinimizedParent
// TODO: this causes XCB error message when being visible the first time
readonly property var winId: toolTipDelegate.isWin && toolTipDelegate.windows[flatIndex] !== undefined ? toolTipDelegate.windows[flatIndex] : 0
PlasmaCore.WindowThumbnail {
id: x11Thumbnail
anchors {
fill: parent
bottomMargin: PlasmaCore.Units.smallSpacing
}
visible: !albumArtImage.visible && !thumbnailSourceItem.isMinimized && Number.isInteger(thumbnailSourceItem.winId)
winId: Number.isInteger(thumbnailSourceItem.winId) ? thumbnailSourceItem.winId : 0
}
Loader {
id: pipeWireLoader
Layout.alignment: Qt.AlignTop
//anchors.fill: hoverHandler
// Indent a little bit so that neither the thumbnail nor the drop
// shadow can cover up the highlight
anchors.margins: PlasmaCore.Units.smallSpacing * 2
active: !albumArtImage.visible && !Number.isInteger(thumbnailSourceItem.winId)
//In a loader since we might not have PipeWire available yet
source: "PipeWireThumbnail.qml"
}
DropShadow {
anchors.fill: pipeWireLoader.active ? pipeWireLoader : x11Thumbnail
visible: pipeWireLoader.active ? pipeWireLoader.item.visible : x11Thumbnail.visible
horizontalOffset: 0
verticalOffset: Math.round(3 * PlasmaCore.Units.devicePixelRatio)
radius: Math.round(2 * PlasmaCore.Units.devicePixelRatio)
samples: Math.round(radius * 1.5)
color: "Black"
source: pipeWireLoader.active ? pipeWireLoader.item : x11Thumbnail
}
Image {
id: albumArtBackground
source: albumArt
sourceSize: Qt.size(x11Thumbnail.width, x11Thumbnail.height)
anchors {
top: x11Thumbnail.top
bottom: x11Thumbnail.bottom
left: parent.left
right: parent.right
}
fillMode: Image.PreserveAspectCrop
visible: albumArtImage.available
asynchronous: true
layer.enabled: true
opacity: 0.5
layer.effect: FastBlur {
source: albumArtBackground
anchors.fill: parent
radius: 30
}
}
// Borders around the blurred album art for added visuals.
PlasmaCore.FrameSvgItem {
id: albumArtBorder
visible: albumArtImage.available
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: -PlasmaCore.Units.smallSpacing/4
imagePath: "widgets/pager"
prefix: "hover"
opacity: 0.4
}
Image {
id: albumArtImage
// also Image.Loading to prevent loading thumbnails just because the album art takes a split second to load
// don't show album art if window title doesn't include media title (eg we're in a different browser tab)
readonly property bool available: (status === Image.Ready || status === Image.Loading) && generateTitle().includes(track)
anchors.fill: x11Thumbnail
// Indent by one pixel to make sure we never cover up the entire highlight
anchors.margins: 1
anchors.topMargin: PlasmaCore.Units.smallSpacing*2;
sourceSize: Qt.size(parent.width, parent.height)
asynchronous: true
source: albumArt
fillMode: Image.PreserveAspectFit
visible: available
}
// when minimized, we don't have a preview on X11, so show the icon
PlasmaCore.IconItem {
width: parent.width
height: thumbnailSourceItem.height
anchors.horizontalCenter: parent.horizontalCenter
source: thumbnailSourceItem.isMinimized && !albumArtImage.visible && Number.isInteger(thumbnailSourceItem.winId) ? icon : ""
animated: false
usesPlasmaTheme: false
visible: valid && !pipeWireLoader.active
}
}
// Player controls row
RowLayout {
id: playerControls
Layout.maximumWidth: header.Layout.maximumWidth
// Match margins of header
Layout.leftMargin: isWin ? 0 : PlasmaCore.Units.gridUnit / 2
Layout.rightMargin: isWin ? 0 : PlasmaCore.Units.gridUnit / 2
//Layout.bottomMargin: isWin ? 0 : PlasmaCore.Units.gridUnit
visible: hasPlayer
enabled: canControl
ColumnLayout {
Layout.fillWidth: true
Layout.topMargin: PlasmaCore.Units.smallSpacing
Layout.bottomMargin: PlasmaCore.Units.smallSpacing*2
Layout.rightMargin: isWin ? PlasmaCore.Units.smallSpacing : PlasmaCore.Units.largeSpacing
Layout.columnSpan: 2
Layout.column: 0
spacing: 0
z: -1
ScrollableTextWrapper {
id: songTextWrapper
Layout.fillWidth: true
Layout.preferredHeight: songText.height
implicitWidth: songText.implicitWidth
Layout.leftMargin: PlasmaCore.Units.smallSpacing*2;
PlasmaComponents3.Label {
id: songText
parent: songTextWrapper
width: parent.width
height: undefined
lineHeight: 1
maximumLineCount: artistText.visible? 1 : 2
wrapMode: Text.NoWrap
elide: parent.state ? Text.ElideNone : Text.ElideRight
text: track || ""
textFormat: Text.PlainText
}
}
ScrollableTextWrapper {
id: artistTextWrapper
z: -1
Layout.fillWidth: true
Layout.preferredHeight: artistText.height
Layout.leftMargin: PlasmaCore.Units.smallSpacing*2;
implicitWidth: artistText.implicitWidth
visible: artistText.text !== ""
PlasmaComponents3.Label {
id: artistText
parent: artistTextWrapper
width: parent.width
height: undefined
wrapMode: Text.NoWrap
lineHeight: 1
elide: parent.state ? Text.ElideNone : Text.ElideRight
text: artist || ""
font: PlasmaCore.Theme.smallestFont
textFormat: Text.PlainText
//color: "white";
}
}
}
RowLayout {
Layout.fillWidth: true
//Layout.topMargin: PlasmaCore.Units.smallSpacing
Layout.bottomMargin: PlasmaCore.Units.smallSpacing
Layout.rightMargin: isWin ? PlasmaCore.Units.smallSpacing*2 : PlasmaCore.Units.largeSpacing
spacing: -1//PlasmaCore.Units.smallSpacing / 4
MediaButton {
id: canGoBackMedia
orientation: "left"
mediaIcon: "previous"
enableButton: canGoBack
onClicked: {
mpris2Source.goPrevious(mprisSourceName)
}
}
MediaButton {
id: playPauseMedia
orientation: "center"
mediaIcon: "pause"
fallbackMediaIcon: "play"
togglePlayPause: playing
enableButton: playing ? canPause : canPlay
onClicked: {
if (!playing) {
mpris2Source.play(mprisSourceName);
} else {
mpris2Source.pause(mprisSourceName);
}
}
}
MediaButton {
id: canGoForwardMedia
enableButton: canGoNext
orientation: "right"
mediaIcon: "skip"
onClicked: mpris2Source.goNext(mprisSourceName)
}
}
}
function generateTitle() {
if (!isWin) {
return genericName != undefined ? genericName : "";
}
var text;
if (isGroup) {
if (model.display === undefined) {
return "";
}
text = model.display.toString();
} else {
text = displayParent;
}
// KWin appends increasing integers in between pointy brackets to otherwise equal window titles.
// In this case save <#number> as counter and delete it at the end of text.
var counter = text.match(/<\d+>\W*$/);
text = text.replace(/\s*<\d+>\W*$/, "");
// Remove appName from the end of text.
var appNameRegex = new RegExp(appName + "$", "i");
text = text.replace(appNameRegex, "");
text = text.replace(/\s*(?:-|—)*\s*$/, "");
// Add counter back at the end.
if (counter !== null) {
if (text === "") {
text = counter;
} else {
text = text + " " + counter;
}
}
// In case the window title had only redundant information (i.e. appName), text is now empty.
// Add a hyphen to indicate that and avoid empty space.
if (text === "") {
text = appName;
}
return text.toString();
}
function generateSubText() {
if (activitiesParent === undefined) {
return "";
}
var subTextEntries = [];
var onAllDesktops = (isGroup ? IsOnAllVirtualDesktops : isOnAllVirtualDesktopsParent) === true;
if (!plasmoid.configuration.showOnlyCurrentDesktop && virtualDesktopInfo.numberOfDesktops > 1) {
var virtualDesktops = isGroup ? VirtualDesktops : virtualDesktopParent;
if (!onAllDesktops && virtualDesktops !== undefined && virtualDesktops.length > 0) {
var virtualDesktopNameList = new Array();
for (var i = 0; i < virtualDesktops.length; ++i) {
virtualDesktopNameList.push(virtualDesktopInfo.desktopNames[virtualDesktopInfo.desktopIds.indexOf(virtualDesktops[i])]);
}
subTextEntries.push(i18nc("Comma-separated list of desktops", "On %1",
virtualDesktopNameList.join(", ")));
} else if (onAllDesktops) {
subTextEntries.push(i18nc("Comma-separated list of desktops", "Pinned to all desktops"));
}
}
var act = isGroup ? Activities : activitiesParent;
if (act === undefined) {
return subTextEntries.join("\n");
}
if (act.length === 0 && activityInfo.numberOfRunningActivities > 1) {
subTextEntries.push(i18nc("Which virtual desktop a window is currently on",
"Available on all activities"));
} else if (act.length > 0) {
var activityNames = [];
for (var i = 0; i < act.length; i++) {
var activity = act[i];
var activityName = activityInfo.activityName(act[i]);
if (activityName === "") {
continue;
}
if (plasmoid.configuration.showOnlyCurrentActivity) {
if (activity !== activityInfo.currentActivity) {
activityNames.push(activityName);
}
} else if (activity !== activityInfo.currentActivity) {
activityNames.push(activityName);
}
}
if (plasmoid.configuration.showOnlyCurrentActivity) {
if (activityNames.length > 0) {
subTextEntries.push(i18nc("Activities a window is currently on (apart from the current one)",
"Also available on %1", activityNames.join(", ")));
}
} else if (activityNames.length > 0) {
subTextEntries.push(i18nc("Which activities a window is currently on",
"Available on %1", activityNames.join(", ")));
}
}
return subTextEntries.join("\n");
}
}