diff --git a/applets/taskmanager/package/contents/ui/ContextMenu.qml b/applets/taskmanager/package/contents/ui/ContextMenu.qml --- a/applets/taskmanager/package/contents/ui/ContextMenu.qml +++ b/applets/taskmanager/package/contents/ui/ContextMenu.qml @@ -25,10 +25,14 @@ import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.taskmanager 0.1 as TaskManager + PlasmaComponents.ContextMenu { id: menu property QtObject mpris2Source + property var modelIndex + readonly property var atm: TaskManager.AbstractTasksModel placement: { if (plasmoid.location == PlasmaCore.Types.LeftEdge) { @@ -43,8 +47,8 @@ minimumWidth: visualParent.width onStatusChanged: { - if (visualParent && visualParent.m.LauncherUrlWithoutIcon != null && status == PlasmaComponents.DialogStatus.Open) { - launcherToggleAction.checked = (tasksModel.launcherPosition(visualParent.m.LauncherUrlWithoutIcon) != -1); + if (visualParent && get(atm.LauncherUrlWithoutIcon) != null && status == PlasmaComponents.DialogStatus.Open) { + launcherToggleAction.checked = (tasksModel.launcherPosition(get(atm.LauncherUrlWithoutIcon)) != -1); activitiesDesktopsMenu.refresh(); } else if (status == PlasmaComponents.DialogStatus.Closed) { @@ -52,8 +56,12 @@ } } + function get(modelProp) { + return tasksModel.data(modelIndex, modelProp) + } + function show() { - loadDynamicLaunchActions(visualParent.m.LauncherUrlWithoutIcon); + loadDynamicLaunchActions(get(atm.LauncherUrlWithoutIcon)); backend.ungrabMouse(visualParent); openRelative(); } @@ -97,10 +105,10 @@ menu.addMenuItem(newSeparator(menu), virtualDesktopsMenuItem); } - // Add Media Player control actions - var sourceName = mpris2Source.sourceNameForLauncherUrl(launcherUrl); + // Add Media Player control actions (but not if grouped) TODO: doch, wenn auf tooltip rechts geklickt!! + var sourceName = mpris2Source.sourceNameForLauncherUrl(launcherUrl, get(atm.AppPid)); - if (sourceName) { + if (sourceName && !(get(atm.LegacyWinIdList) != undefined && get(atm.LegacyWinIdList).length > 1)) { var playerData = mpris2Source.data[sourceName] if (playerData.CanControl) { @@ -186,9 +194,9 @@ id: virtualDesktopsMenuItem visible: virtualDesktopInfo.numberOfDesktops > 1 - && (visualParent && visualParent.m.IsLauncher !== true - && visualParent.m.IsStartup !== true - && visualParent.m.IsVirtualDesktopChangeable === true) + && (visualParent && get(atm.IsLauncher) !== true + && get(atm.IsStartup) !== true + && get(atm.IsVirtualDesktopChangeable) === true) enabled: visible @@ -216,20 +224,20 @@ var menuItem = menu.newMenuItem(virtualDesktopsMenu); menuItem.text = i18n("Move &To Current Desktop"); menuItem.enabled = Qt.binding(function() { - return menu.visualParent && menu.visualParent.m.VirtualDesktop != virtualDesktopInfo.currentDesktop; + return menu.visualParent && menu.get(atm.VirtualDesktop) != virtualDesktopInfo.currentDesktop; }); menuItem.clicked.connect(function() { - tasksModel.requestVirtualDesktop(menu.visualParent.modelIndex(), virtualDesktopInfo.currentDesktop); + tasksModel.requestVirtualDesktop(menu.modelIndex, virtualDesktopInfo.currentDesktop); }); menuItem = menu.newMenuItem(virtualDesktopsMenu); menuItem.text = i18n("&All Desktops"); menuItem.checkable = true; menuItem.checked = Qt.binding(function() { - return menu.visualParent && menu.visualParent.m.IsOnAllVirtualDesktops === true; + return menu.visualParent && menu.get(atm.IsOnAllVirtualDesktops) === true; }); menuItem.clicked.connect(function() { - tasksModel.requestVirtualDesktop(menu.visualParent.modelIndex(), 0); + tasksModel.requestVirtualDesktop(menu.modelIndex, 0); }); backend.setActionGroup(menuItem.action); @@ -240,10 +248,10 @@ menuItem.text = i18nc("1 = number of desktop, 2 = desktop name", "&%1 Desktop %2", i + 1, virtualDesktopInfo.desktopNames[i]); menuItem.checkable = true; menuItem.checked = Qt.binding((function(i) { - return function() { return menu.visualParent && menu.visualParent.m.VirtualDesktop == (i + 1) }; + return function() { return menu.visualParent && menu.get(atm.VirtualDesktop) == (i + 1) }; })(i)); menuItem.clicked.connect((function(i) { - return function() { return tasksModel.requestVirtualDesktop(menu.visualParent.modelIndex(), i + 1); }; + return function() { return tasksModel.requestVirtualDesktop(menu.modelIndex, i + 1); }; })(i)); backend.setActionGroup(menuItem.action); } @@ -253,7 +261,7 @@ menuItem = menu.newMenuItem(virtualDesktopsMenu); menuItem.text = i18n("&New Desktop"); menuItem.clicked.connect(function() { - tasksModel.requestVirtualDesktop(menu.visualParent.modelIndex(), virtualDesktopInfo.numberOfDesktops + 1) + tasksModel.requestVirtualDesktop(menu.modelIndex, virtualDesktopInfo.numberOfDesktops + 1) }); } @@ -265,8 +273,8 @@ id: activitiesDesktopsMenuItem visible: activityInfo.numberOfRunningActivities > 1 - && (visualParent && !visualParent.m.IsLauncher - && !visualParent.m.IsStartup) + && (visualParent && !get(atm.IsLauncher) + && !get(atm.IsStartup)) enabled: visible @@ -293,18 +301,18 @@ var menuItem = menu.newMenuItem(activitiesDesktopsMenu); menuItem.text = i18n("Add To Current Activity"); menuItem.enabled = Qt.binding(function() { - return menu.visualParent && menu.visualParent.m.Activities.length > 0 && - menu.visualParent.m.Activities.indexOf(activityInfo.currentActivity) < 0; + return menu.visualParent && menu.get(atm.Activities).length > 0 && + menu.get(atm.Activities).indexOf(activityInfo.currentActivity) < 0; }); menuItem.clicked.connect(function() { - tasksModel.requestActivities(menu.visualParent.modelIndex(), menu.visualParent.m.Activities.concat(activityInfo.currentActivity)); + tasksModel.requestActivities(menu.modelIndex, menu.get(atm.Activities).concat(activityInfo.currentActivity)); }); menuItem = menu.newMenuItem(activitiesDesktopsMenu); menuItem.text = i18n("All Activities"); menuItem.checkable = true; menuItem.checked = Qt.binding(function() { - return menu.visualParent && menu.visualParent.m.Activities.length === 0; + return menu.visualParent && menu.get(atm.Activities).length === 0; }); menuItem.clicked.connect(function() { var checked = menuItem.checked; @@ -312,7 +320,7 @@ if (!checked) { newActivities = new Array(activityInfo.currentActivity); } - tasksModel.requestActivities(menu.visualParent.modelIndex(), newActivities); + tasksModel.requestActivities(menu.modelIndex, newActivities); }); menu.newSeparator(activitiesDesktopsMenu); @@ -326,13 +334,13 @@ menuItem.checkable = true; menuItem.checked = Qt.binding( (function(activityId) { return function() { - return menu.visualParent && menu.visualParent.m.Activities.indexOf(activityId) >= 0; + return menu.visualParent && menu.get(atm.Activities).indexOf(activityId) >= 0; }; })(activityId)); menuItem.clicked.connect((function(activityId) { return function () { var checked = menuItem.checked; - var newActivities = menu.visualParent.m.Activities; + var newActivities = menu.get(atm.Activities); if (checked) { newActivities = newActivities.concat(activityId); } else { @@ -342,7 +350,7 @@ } newActivities = newActivities.splice(index, 1); } - return tasksModel.requestActivities(menu.visualParent.modelIndex(), newActivities); + return tasksModel.requestActivities(menu.modelIndex, newActivities); }; })(activityId)); } @@ -356,63 +364,63 @@ PlasmaComponents.MenuItem { - visible: (visualParent && visualParent.m.IsLauncher !== true && visualParent.m.IsStartup !== true) + visible: (visualParent && get(atm.IsLauncher) !== true && get(atm.IsStartup) !== true) - enabled: visualParent && visualParent.m.IsMinimizable === true + enabled: visualParent && get(atm.IsMinimizable) === true checkable: true - checked: visualParent && visualParent.m.IsMinimized === true + checked: visualParent && get(atm.IsMinimized) === true text: i18n("Mi&nimize") - onClicked: tasksModel.requestToggleMinimized(visualParent.modelIndex()) + onClicked: tasksModel.requestToggleMinimized(modelIndex) } PlasmaComponents.MenuItem { - visible: (visualParent && visualParent.m.IsLauncher !== true && visualParent.m.IsStartup !== true) + visible: (visualParent && get(atm.IsLauncher) !== true && get(atm.IsStartup) !== true) - enabled: visualParent && visualParent.m.IsMaximizable === true + enabled: visualParent && get(atm.IsMaximizable) === true checkable: true - checked: visualParent && visualParent.m.IsMaximized === true + checked: visualParent && get(atm.IsMaximized) === true text: i18n("Ma&ximize") - onClicked: tasksModel.requestToggleMaximized(visualParent.modelIndex()) + onClicked: tasksModel.requestToggleMaximized(modelIndex) } PlasmaComponents.MenuItem { id: startNewInstanceItem - visible: (visualParent && visualParent.m.IsLauncher !== true && visualParent.m.IsStartup !== true) + visible: (visualParent && get(atm.IsLauncher) !== true && get(atm.IsStartup) !== true) - enabled: visualParent && visualParent.m.LauncherUrlWithoutIcon != null + enabled: visualParent && get(atm.LauncherUrlWithoutIcon) != null text: i18n("Start New Instance") icon: "system-run" - onClicked: tasksModel.requestNewInstance(visualParent.modelIndex()) + onClicked: tasksModel.requestNewInstance(modelIndex) } PlasmaComponents.MenuItem { id: launcherToggleAction visible: visualParent - && visualParent.m.IsLauncher !== true - && visualParent.m.IsStartup !== true + && get(atm.IsLauncher) !== true + && get(atm.IsStartup) !== true && plasmoid.immutability !== PlasmaCore.Types.SystemImmutable && (activityInfo.numberOfRunningActivities < 2) - enabled: visualParent && visualParent.m.LauncherUrlWithoutIcon != "" + enabled: visualParent && get(atm.LauncherUrlWithoutIcon) != "" checkable: true text: i18n("&Show A Launcher When Not Running") onClicked: { - if (tasksModel.launcherPosition(visualParent.m.LauncherUrlWithoutIcon) != -1) { - tasksModel.requestRemoveLauncher(visualParent.m.LauncherUrlWithoutIcon); + if (tasksModel.launcherPosition(get(atm.LauncherUrlWithoutIcon)) != -1) { + tasksModel.requestRemoveLauncher(get(atm.LauncherUrlWithoutIcon)); } else { - tasksModel.requestAddLauncher(visualParent.m.LauncherUrl); + tasksModel.requestAddLauncher(get(atm.LauncherUrl)); } } } @@ -423,8 +431,8 @@ text: i18n("&Show A Launcher When Not Running") visible: visualParent - && visualParent.m.IsLauncher !== true - && visualParent.m.IsStartup !== true + && get(atm.IsLauncher) !== true + && get(atm.IsStartup) !== true && plasmoid.immutability !== PlasmaCore.Types.SystemImmutable && (activityInfo.numberOfRunningActivities >= 2) @@ -466,7 +474,7 @@ if (menu.visualParent === null) return; - var url = menu.visualParent.m.LauncherUrlWithoutIcon; + var url = menu.get(atm.LauncherUrlWithoutIcon); var activities = tasksModel.launcherActivities(url); @@ -497,18 +505,18 @@ } PlasmaComponents.MenuItem { - visible: (visualParent && visualParent.m.IsLauncher === true) && plasmoid.immutability !== PlasmaCore.Types.SystemImmutable + visible: (visualParent && get(atm.IsLauncher) === true) && plasmoid.immutability !== PlasmaCore.Types.SystemImmutable text: i18n("Remove Launcher") - onClicked: tasksModel.requestRemoveLauncher(visualParent.m.LauncherUrlWithoutIcon); + onClicked: tasksModel.requestRemoveLauncher(get(atm.LauncherUrlWithoutIcon)); } PlasmaComponents.MenuItem { id: moreActionsMenuItem - visible: (visualParent && visualParent.m.IsLauncher !== true && visualParent.m.IsStartup !== true) + visible: (visualParent && get(atm.IsLauncher) !== true && get(atm.IsStartup) !== true) enabled: visible @@ -518,63 +526,63 @@ visualParent: moreActionsMenuItem.action PlasmaComponents.MenuItem { - enabled: menu.visualParent && menu.visualParent.m.IsMovable === true + enabled: menu.visualParent && menu.get(atm.IsMovable) === true text: i18n("&Move") icon: "transform-move" - onClicked: tasksModel.requestMove(menu.visualParent.modelIndex()) + onClicked: tasksModel.requestMove(menu.modelIndex) } PlasmaComponents.MenuItem { - enabled: menu.visualParent && menu.visualParent.m.IsResizable === true + enabled: menu.visualParent && menu.get(atm.IsResizable) === true text: i18n("Re&size") - onClicked: tasksModel.requestResize(menu.visualParent.modelIndex()) + onClicked: tasksModel.requestResize(menu.modelIndex) } PlasmaComponents.MenuItem { checkable: true - checked: menu.visualParent && menu.visualParent.m.IsKeepAbove === true + checked: menu.visualParent && menu.get(atm.IsKeepAbove) === true text: i18n("Keep &Above Others") icon: "go-up" - onClicked: tasksModel.requestToggleKeepAbove(menu.visualParent.modelIndex()) + onClicked: tasksModel.requestToggleKeepAbove(menu.modelIndex) } PlasmaComponents.MenuItem { checkable: true - checked: menu.visualParent && menu.visualParent.m.IsKeepBelow === true + checked: menu.visualParent && menu.get(atm.IsKeepBelow) === true text: i18n("Keep &Below Others") icon: "go-down" - onClicked: tasksModel.requestToggleKeepBelow(menu.visualParent.modelIndex()) + onClicked: tasksModel.requestToggleKeepBelow(menu.modelIndex) } PlasmaComponents.MenuItem { - enabled: menu.visualParent && menu.visualParent.m.IsFullScreenable === true + enabled: menu.visualParent && menu.get(atm.IsFullScreenable) === true checkable: true - checked: menu.visualParent && menu.visualParent.m.IsFullScreen === true + checked: menu.visualParent && menu.get(atm.IsFullScreen) === true text: i18n("&Fullscreen") icon: "view-fullscreen" - onClicked: tasksModel.requestToggleFullScreen(menu.visualParent.modelIndex()) + onClicked: tasksModel.requestToggleFullScreen(menu.modelIndex) } PlasmaComponents.MenuItem { - enabled: menu.visualParent && menu.visualParent.m.IsShadeable === true + enabled: menu.visualParent && menu.get(atm.IsShadeable) === true checkable: true - checked: menu.visualParent && menu.visualParent.m.IsShaded === true + checked: menu.visualParent && menu.get(atm.IsShaded) === true text: i18n("&Shade") - onClicked: tasksModel.requestToggleShaded(menu.visualParent.modelIndex()) + onClicked: tasksModel.requestToggleShaded(menu.modelIndex) } PlasmaComponents.MenuItem { @@ -582,14 +590,14 @@ } PlasmaComponents.MenuItem { - visible: (plasmoid.configuration.groupingStrategy != 0) && menu.visualParent.m.IsWindow === true + visible: (plasmoid.configuration.groupingStrategy != 0) && menu.get(atm.IsWindow) === true checkable: true - checked: menu.visualParent && menu.visualParent.m.IsGroupable === true + checked: menu.visualParent && menu.get(atm.IsGroupable) === true text: i18n("Allow this program to be grouped") - onClicked: tasksModel.requestToggleGrouping(menu.visualParent.modelIndex()) + onClicked: tasksModel.requestToggleGrouping(menu.modelIndex) } } } @@ -613,13 +621,13 @@ PlasmaComponents.MenuItem { id: closeWindowItem - visible: (visualParent && visualParent.m.IsLauncher !== true && visualParent.m.IsStartup !== true) + visible: (visualParent && get(atm.IsLauncher) !== true && get(atm.IsStartup) !== true) - enabled: visualParent && visualParent.m.IsClosable === true + enabled: visualParent && get(atm.IsClosable) === true text: i18n("&Close") icon: "window-close" - onClicked: tasksModel.requestClose(visualParent.modelIndex()) + onClicked: tasksModel.requestClose(modelIndex) } } diff --git a/applets/taskmanager/package/contents/ui/Task.qml b/applets/taskmanager/package/contents/ui/Task.qml --- a/applets/taskmanager/package/contents/ui/Task.qml +++ b/applets/taskmanager/package/contents/ui/Task.qml @@ -74,6 +74,14 @@ } onItemIndexChanged: { + // Part of the workaround described in toolTipLoader. When removed + // we need to call toolTipLoader.hideToolTipTemporarily() instead. + // Otherwise the tooltip won't hide when manually reordering tasks. + toolTipLoader.needsReload = true; + taskRepeater.delayedReloadToolTip(); + // + // + if (!inPopup && !tasks.vertical && (LayoutManager.calculateStripes() > 1 || !plasmoid.configuration.separateLaunchers)) { tasks.requestLayout(); @@ -83,7 +91,7 @@ onContainsMouseChanged: { if (containsMouse) { if (inPopup) { - forceActiveFocus() + forceActiveFocus(); } } else { pressed = false; @@ -92,6 +100,7 @@ if (model.IsWindow === true) { tasks.windowsHovered(model.LegacyWinIdList, containsMouse); } + } onPressed: { @@ -100,11 +109,7 @@ pressX = mouse.x; pressY = mouse.y; } else if (mouse.button == Qt.RightButton) { - if (plasmoid.configuration.showToolTips) { - toolTip.hideToolTip(); - } - - tasks.createContextMenu(task).show(); + tasks.createContextMenu(task, modelIndex()).show(); } } @@ -121,7 +126,8 @@ } else if (mouse.button == Qt.LeftButton) { TaskTools.activateTask(modelIndex(), model, mouse.modifiers, task); if (plasmoid.configuration.showToolTips) { - toolTip.hideToolTip(); + // Hide the tooltip, but on cursor movement in mousearea show it again (with cooldown). + toolTipLoader.hideToolTipTemporarily(); } } } @@ -219,84 +225,59 @@ }); } - PlasmaCore.ToolTipArea { - id: toolTip - + Loader { + id: toolTipLoader + sourceComponent: plasmoid.configuration.showToolTips ? toolTip : undefined anchors.fill: parent - active: !inPopup && !groupDialog.visible && plasmoid.configuration.showToolTips - interactive: true - location: plasmoid.location - - mainItem: toolTipDelegate - - onContainsMouseChanged: { - if (containsMouse) { - toolTipDelegate.parentIndex = itemIndex; - - toolTipDelegate.windows = Qt.binding(function() { - return model.LegacyWinIdList; - }); - toolTipDelegate.mainText = Qt.binding(function() { - return model.display; - }); - toolTipDelegate.icon = Qt.binding(function() { - return model.decoration; - }); - toolTipDelegate.subText = Qt.binding(function() { - return model.IsLauncher === true ? model.GenericName : toolTip.generateSubText(model); - }); - toolTipDelegate.launcherUrl = Qt.binding(function() { - return model.LauncherUrlWithoutIcon; - }); + function hideToolTipTemporarily() { + active = false; + active = true; + } + + // Workaround, see: https://bugreports.qt.io/browse/QTBUG-47523 + // https://bugreports.qt.io/browse/QTBUG-55864 + // + // In DelegateModel the submodel index is currently not updating + // correctly, when the parent index is changed, which means that + // on reordering of the root tasks grouped tasks might show the + // content of the tasks, which were previously listed under this + // parent index. + // In order to solve this issue until DelegateModel is fixed + // upstream, we reinstinate every (also without group, since they + // can get a group and would crash then) tooltip with a slight + // delay (see taskRepeater.delayedReloadToolTip()) if its parent + // index was changed. + // + property bool needsReload: false + function reload() { + if (!needsReload) { + needsReload = false; + return; } + active = false; + active = true; + needsReload = false; } + Component.onCompleted: taskRepeater.reloadToolTips.connect(reload) + // + // + } - function generateSubText(task) { - var subTextEntries = new Array(); + Component { + id: toolTip - if (!plasmoid.configuration.showOnlyCurrentDesktop - && virtualDesktopInfo.numberOfDesktops > 1 - && model.IsOnAllVirtualDesktops !== true - && model.VirtualDesktop != -1 - && model.VirtualDesktop != undefined) { - subTextEntries.push(i18n("On %1", virtualDesktopInfo.desktopNames[model.VirtualDesktop - 1])); - } + PlasmaCore.ToolTipArea { + anchors.fill: parent - if (model.Activities == undefined) { - return subTextEntries.join("\n"); - } + active: !inPopup && !groupDialog.visible && plasmoid.configuration.showToolTips + interactive: true + location: plasmoid.location - if (model.Activities.length == 0 && activityInfo.numberOfRunningActivities > 1) { - subTextEntries.push(i18nc("Which virtual desktop a window is currently on", - "Available on all activities")); - } else if (model.Activities.length > 0) { - var activityNames = new Array(); - - for (var i = 0; i < model.Activities.length; i++) { - var activity = model.Activities[i]; - - if (plasmoid.configuration.showOnlyCurrentActivity) { - if (activity != activityInfo.currentActivity) { - activityNames.push(activityInfo.activityName(model.Activities[i])); - } - } else if (activity != activityInfo.currentActivity) { - activityNames.push(activityInfo.activityName(model.Activities[i])); - } - } - - 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(", "))); - } + mainItem: ToolTipDelegate { + id: toolTipDelegate + parentTask: task } - - return subTextEntries.join("\n"); } } } diff --git a/applets/taskmanager/package/contents/ui/ToolTipDelegate.qml b/applets/taskmanager/package/contents/ui/ToolTipDelegate.qml --- a/applets/taskmanager/package/contents/ui/ToolTipDelegate.qml +++ b/applets/taskmanager/package/contents/ui/ToolTipDelegate.qml @@ -2,6 +2,7 @@ * Copyright 2013 by Sebastian Kügler * Copyright 2014 by Martin Gräßlin * Copyright 2016 by Kai Uwe Broulik +* Copyright 2016 by Roman Gilg * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as @@ -19,335 +20,520 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. */ -import QtQuick 2.0 +import QtQuick 2.7 import QtQuick.Layouts 1.1 import QtGraphicalEffects 1.0 +import QtQml.Models 2.2 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.plasma.extras 2.0 as PlasmaExtras import org.kde.kquickcontrolsaddons 2.0 as KQuickControlsAddons -Column { - id: tooltipContentItem +import org.kde.taskmanager 0.1 as TaskManager - property Item toolTip - property var parentIndex - property var windows - property string mainText - property string subText - property variant icon - property url launcherUrl - property bool group: (windows !== undefined && windows.length > 1) - - readonly property string mprisSourceName: mpris2Source.sourceNameForLauncherUrl(launcherUrl) - readonly property bool hasPlayer: !!mprisSourceName && !!playerData - - readonly property var playerData: mpris2Source.data[mprisSourceName] - readonly property bool playing: hasPlayer && playerData.PlaybackStatus === "Playing" - readonly property bool canControl: hasPlayer && playerData.CanControl - 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 : ({}) - - 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"] || "" +PlasmaExtras.ScrollArea { + id: tooltipContentItem - readonly property int thumbnailWidth: units.gridUnit * 15 - readonly property int thumbnailHeight: units.gridUnit * 10 + property Item parentTask + readonly property bool isGroup: parentTask.m.IsGroupParent == true + readonly property var windows: parentTask.m.LegacyWinIdList + readonly property string appName: parentTask.m.AppName + readonly property int pid: parentTask.m.AppPid ? parentTask.m.AppPid : -1 + readonly property variant icon: parentTask.m.decoration + readonly property url launcherUrl: parentTask.m.LauncherUrlWithoutIcon + readonly property bool isLauncher: parentTask.m.IsLauncher == true + readonly property bool isWin: windows !== undefined - property int preferredTextWidth: theme.mSize(theme.defaultFont).width * 30 + readonly property bool isVerticalPanel: plasmoid.formFactor == PlasmaCore.Types.Vertical - Layout.minimumWidth: Math.max(thumbnailWidth, windowRow.width, appLabelRow.width) + units.largeSpacing / 2 - Layout.minimumHeight: childrenRect.height + units.largeSpacing + Layout.minimumWidth: contentLoader.width Layout.maximumWidth: Layout.minimumWidth + + Layout.minimumHeight: contentLoader.height Layout.maximumHeight: Layout.minimumHeight LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft LayoutMirroring.childrenInherit: true - spacing: units.largeSpacing / 2 + property int textWidth: theme.mSize(theme.defaultFont).width * 20 - states: State { - when: tooltipContentItem.hasPlayer + verticalScrollBarPolicy: Qt.ScrollBarAlwaysOff + horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff - PropertyChanges { - target: thumbnailSourceItem - opacity: 0 // cannot set visible to false or else WindowThumbnail won't provide thumbnail + Component.onCompleted: { + flickableItem.interactive = Qt.binding(function() { + return isVerticalPanel ? contentLoader.height > viewport.height : contentLoader.width > viewport.width + }); + } + + Item { + width: contentLoader.status == Loader.Ready ? contentLoader.width : 1 + height: contentLoader.status == Loader.Ready ? contentLoader.height : 1 + + Loader { + id: contentLoader + sourceComponent: isWin && !isGroup || isLauncher ? tooltipInstance : contentGrid } - PropertyChanges { - target: playerControlsOpacityMask - visible: true - source: thumbnailSourceItem - maskSource: playerControlsShadowMask + + Component { + id: contentGrid + Grid { + rows: !isVerticalPanel + columns: isVerticalPanel + flow: isVerticalPanel ? Grid.TopToBottom : Grid.LeftToRight + spacing: units.largeSpacing + + Repeater { + id: groupRepeater + model: groupModel + } + } } - PropertyChanges { - target: playerControlsRow - visible: true + + DelegateModel { + id: groupModel + model: tasksModel + rootIndex: tasksModel.makeModelIndex(parentTask.itemIndex, -1) + delegate: tooltipInstance } - } - Item { - id: thumbnailContainer - width: Math.max(parent.width, windowRow.width) - height: albumArtImage.available ? albumArtImage.height : - raisePlayerArea.visible ? raisePlayerArea.height : - windowRow.height + // + // text labels + thumbnail + Component { + id: tooltipInstance + Column { + property var submodelIndex: index != undefined ? tasksModel.makeModelIndex(parentTask.itemIndex, isGroup ? index : -1) : 0 + property int flatIndex: isGroup && index != undefined ? index : 0 + spacing: units.smallSpacing + + property url launcherUrl: tooltipContentItem.launcherUrl ? tooltipContentItem.launcherUrl : "" + + property bool mpris2SourceDefined: mpris2Source != undefined + property string mprisSourceName: "" + property var playerData: mprisSourceName != "" ? mpris2Source.data[mprisSourceName] : 0 + property bool hasPlayer: !!mprisSourceName && !!playerData + property bool playing: hasPlayer && playerData.PlaybackStatus === "Playing" + property bool canControl: hasPlayer && playerData.CanControl + property bool canGoBack: hasPlayer && playerData.CanGoPrevious + property bool canGoNext: hasPlayer && playerData.CanGoNext + property bool canRaise: hasPlayer && playerData.CanRaise + property var currentMetadata: hasPlayer ? playerData.Metadata : ({}) + + 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"] || "" - Item { - id: thumbnailSourceItem - anchors.fill: parent + function checkPlayer() { + if (!mpris2SourceDefined) { + return; + } + // set mprisSourceName here, otherwise we call sourceNameForLauncherUrl on every service update again (currentMetaData!) + mprisSourceName = mpris2Source.sourceNameForLauncherUrl(launcherUrl, isGroup == true ? AppPid : pid); + } - PlasmaExtras.ScrollArea { - id: scrollArea - anchors.horizontalCenter: parent.horizontalCenter - width: Math.max(windowRow.width, thumbnailWidth) - height: parent.height + // + // launcher icon + text labels + close button + RowLayout { + id: header + Layout.minimumWidth: childrenRect.width + Layout.maximumWidth: Layout.minimumWidth - visible: !albumArtImage.available + Layout.minimumHeight: childrenRect.height + Layout.maximumHeight: Layout.minimumHeight - verticalScrollBarPolicy: Qt.ScrollBarAlwaysOff - horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff + anchors.horizontalCenter: parent.horizontalCenter - Component.onCompleted: { - flickableItem.interactive = Qt.binding(function() { - return contentItem.width > viewport.width; - }); + // launcher icon + PlasmaCore.IconItem { + source: icon + animated: false + usesPlasmaTheme: false + visible: !isWin + } + // all textlabels + Column { + spacing: 0.75 * units.smallSpacing + PlasmaComponents.Label { + width: isWin ? textWidth : undefined + height: theme.mSize(theme.defaultFont).height + font.pointSize: -1 + font.pixelSize: height + elide: Text.ElideRight + text: appName + opacity: flatIndex == 0 + textFormat: Text.PlainText + } + // window title + PlasmaComponents.Label { + width: isWin ? textWidth : undefined + height: 0.75 * theme.mSize(theme.defaultFont).height + font.pointSize: -1 + font.pixelSize: height + elide: Text.ElideRight + text: generateTitle() + textFormat: Text.PlainText + opacity: 0.75 + } + // subtext + PlasmaComponents.Label { + width: isWin ? textWidth : undefined + height: 0.6 * theme.mSize(theme.defaultFont).height + font.pointSize: -1 + font.pixelSize: height + elide: Text.ElideRight + text: isWin ? generateSubText() : "" + textFormat: Text.PlainText + opacity: 0.6 + visible: text !== "" + } + } + // close button + MouseArea { + Layout.alignment: Qt.AlignRight | Qt.AlignTop + + height: units.iconSizes.smallMedium + width: height + + visible: isWin + + acceptedButtons: Qt.LeftButton + hoverEnabled: true + onClicked: { + backend.cancelHighlightWindows(); + tasksModel.requestClose(submodelIndex); + } + + PlasmaCore.IconItem { + anchors.fill: parent + active: parent.containsMouse + + source: "window-close" + animated: false + } + } } - Row { - id: windowRow - spacing: units.largeSpacing + // thumbnail container + Item { + id: thumbnail + width: header.width + // similar to 0.5625 = 1 / (16:9) as most screens are + // round necessary, otherwise shadow mask for players has gap! + height: Math.round(0.5 * width) + + visible: isWin - Repeater { - model: plasmoid.configuration.showToolTips && !albumArtImage.available ? windows : null + anchors.horizontalCenter: parent.horizontalCenter + + Item { + id: thumbnailSourceItem + anchors.fill: parent + + property int winId: isWin && windows[flatIndex] != undefined ? windows[flatIndex] : 0 PlasmaCore.WindowThumbnail { id: windowThumbnail + anchors.fill: parent - width: thumbnailWidth - height: thumbnailHeight + visible: !albumArtImage.visible && IsMinimized == false - winId: modelData + // TODO: this causes XCB error message on first emergence + winId: thumbnailSourceItem.winId ToolTipWindowMouseArea { anchors.fill: parent - modelIndex: tasksModel.makeModelIndex(parentIndex, group ? index : -1) - winId: modelData - thumbnailItem: parent + rootTask: parentTask + modelIndex: submodelIndex + winId: thumbnailSourceItem.winId + } + } + + Image { + id: albumArtImage + // also Image.Loading to prevent loading thumbnails just because the album art takes a split second to load + readonly property bool available: status === Image.Ready || status === Image.Loading + + anchors.fill: parent + sourceSize: Qt.size(parent.width, parent.height) + asynchronous: true + source: albumArt + fillMode: Image.PreserveAspectCrop + visible: available + + ToolTipWindowMouseArea { + anchors.fill: parent + rootTask: parentTask + modelIndex: submodelIndex + winId: thumbnailSourceItem.winId + } + } + + // when minimized, we don't have a preview, so show the icon + PlasmaCore.IconItem { + anchors.fill: parent + source: icon + animated: false + usesPlasmaTheme: false + visible: IsMinimized == true && !albumArtImage.visible + + ToolTipWindowMouseArea { + anchors.fill: parent + rootTask: parentTask + modelIndex: submodelIndex + winId: thumbnailSourceItem.winId } } } - } - } - Image { - id: albumArtImage - // also Image.Loading to prevent loading thumbnails just because the album art takes a split second to load - readonly property bool available: status === Image.Ready || status === Image.Loading - - anchors.centerIn: parent - width: parent.width - height: thumbnailHeight - sourceSize: Qt.size(thumbnailWidth, thumbnailHeight) - asynchronous: true - source: albumArt - fillMode: Image.PreserveAspectCrop - visible: available - - ToolTipWindowMouseArea { - anchors.fill: parent - modelIndex: tasksModel.makeModelIndex(parentIndex, group ? index : -1) - winId: windows != undefined ? (windows[0] || 0) : 0 - } - } - MouseArea { - id: raisePlayerArea - anchors.centerIn: parent - width: thumbnailWidth - height: thumbnailHeight - - // if there's no window associated with this task, we might still be able to raise the player - visible: windows == undefined || !windows[0] && canRaise - onClicked: mpris2Source.raise(mprisSourceName) - - PlasmaCore.IconItem { - anchors.fill: parent - source: icon - animated: false - usesPlasmaTheme: false - visible: !albumArtImage.available - } - } - } + Loader { + anchors.fill: thumbnail + sourceComponent: hasPlayer ? playerControlsComp : undefined + } - Item { - id: playerControlsShadowMask - anchors.fill: thumbnailSourceItem - visible: false // OpacityMask would render it + Component { + id: playerControlsComp - Rectangle { - width: parent.width - height: parent.height - playerControlsRow.height - } + Item { + anchors.fill: parent - Rectangle { - anchors.bottom: parent.bottom - width: parent.width - height: playerControlsRow.height - opacity: 0.2 - } - } + // TODO: When could this really be the case? A not-launcher-task always has a window!? + // if there's no window associated with this task, we might still be able to raise the player + // MouseArea { + // id: raisePlayerArea + // anchors.fill: parent - OpacityMask { - id: playerControlsOpacityMask - anchors.fill: thumbnailSourceItem - visible: false - } + // visible: !isWin || !windows[0] && canRaise + // onClicked: mpris2Source.raise(mprisSourceName) + // } - // prevent accidental click-through when a control is disabled - MouseArea { - anchors.fill: playerControlsRow - enabled: playerControlsRow.visible - } + Item { + id: playerControlsFrostedGlass + anchors.fill: parent + visible: false // OpacityMask would render it + + Rectangle { + width: parent.width + height: parent.height - playerControlsRow.height + opacity: 0 + } + + Rectangle { + anchors.bottom: parent.bottom + width: parent.width + height: playerControlsRow.height + opacity: 0.8 + } + } - RowLayout { - id: playerControlsRow - anchors { - horizontalCenter: parent.horizontalCenter - bottom: thumbnailSourceItem.bottom - } - width: thumbnailWidth - spacing: 0 - enabled: canControl - visible: false - - ColumnLayout { - Layout.fillWidth: true - spacing: 0 - - PlasmaExtras.Heading { - Layout.fillWidth: true - level: 4 - wrapMode: Text.NoWrap - elide: Text.ElideRight - text: track || "" + OpacityMask { + id: playerControlsOpacityMask + anchors.fill: parent + source: playerControlsFrostedGlass + maskSource: thumbnailSourceItem + } + + // prevent accidental click-through when a control is disabled + MouseArea { + anchors.fill: playerControlsRow + } + + RowLayout { + id: playerControlsRow + anchors { + horizontalCenter: parent.horizontalCenter + bottom: parent.bottom + } + width: parent.width + spacing: 0 + enabled: canControl + + ColumnLayout { + Layout.fillWidth: true + spacing: 0 + + PlasmaExtras.Heading { + Layout.fillWidth: true + level: 4 + wrapMode: Text.NoWrap + elide: Text.ElideRight + text: track || "" + } + + PlasmaExtras.Heading { + Layout.fillWidth: true + level: 5 + wrapMode: Text.NoWrap + elide: Text.ElideRight + text: artist || "" + } + } + + MouseArea { + height: units.iconSizes.smallMedium + width: height + enabled: canGoBack + + acceptedButtons: Qt.LeftButton + hoverEnabled: true + onClicked: mpris2Source.goPrevious(mprisSourceName) + + PlasmaCore.IconItem { + anchors.fill: parent + enabled: canGoBack + active: parent.containsMouse + + source: LayoutMirroring.enabled ? "media-skip-forward" : "media-skip-backward" + animated: false + } + } + + MouseArea { + height: units.iconSizes.medium + width: height + + acceptedButtons: Qt.LeftButton + hoverEnabled: true + onClicked: mpris2Source.playPause(mprisSourceName) + + PlasmaCore.IconItem { + anchors.fill: parent + active: parent.containsMouse + + source: playing ? "media-playback-pause" : "media-playback-start" + animated: false + } + } + + MouseArea { + height: units.iconSizes.smallMedium + width: height + enabled: canGoNext + + acceptedButtons: Qt.LeftButton + hoverEnabled: true + onClicked: mpris2Source.goNext(mprisSourceName) + + PlasmaCore.IconItem { + anchors.fill: parent + enabled: canGoNext + active: parent.containsMouse + + source: LayoutMirroring.enabled ? "media-skip-backward" : "media-skip-forward" + animated: false + } + } + } + } + } } - PlasmaExtras.Heading { - Layout.fillWidth: true - level: 5 - wrapMode: Text.NoWrap - elide: Text.ElideRight - text: artist || "" + Component.onCompleted: { + checkPlayer() + mpris2Source.sourceAdded.connect(checkPlayer) } - } - PlasmaComponents.ToolButton { - enabled: canGoBack - iconName: LayoutMirroring.enabled ? "media-skip-forward" : "media-skip-backward" - tooltip: i18nc("Go to previous song", "Previous") - Accessible.name: tooltip - onClicked: mpris2Source.goPrevious(mprisSourceName) - } + function generateTitle() { + if (!isWin || model.display == undefined) { + return parentTask.m.GenericName != undefined ? parentTask.m.GenericName : ""; + } - PlasmaComponents.ToolButton { - Layout.fillHeight: true - Layout.preferredWidth: height // make this button bigger - iconName: playing ? "media-playback-pause" : "media-playback-start" - tooltip: playing ? i18nc("Pause player", "Pause") : i18nc("Start player", "Play") - Accessible.name: tooltip - onClicked: mpris2Source.playPause(mprisSourceName) - } + var text = model.display.toString(); - PlasmaComponents.ToolButton { - enabled: canGoNext - iconName: LayoutMirroring.enabled ? "media-skip-backward" : "media-skip-forward" - tooltip: i18nc("Go to next song", "Next") - Accessible.name: tooltip - onClicked: mpris2Source.goNext(mprisSourceName) - } - } - } + // 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; + } + } - Row { - id: appLabelRow - anchors.left: parent.left - spacing: units.largeSpacing - - Item { - id: imageContainer - width: tooltipIcon.width - height: tooltipIcon.height - - PlasmaCore.IconItem { - id: tooltipIcon - anchors { - left: parent.left - leftMargin: units.largeSpacing / 2 + // In case the window title had only redundant informations (i.e. appName), text is now empty. + // Add a hyphen to indicate that and avoid empty space. + if (text == "") { + text = "—"; + } + return text.toString(); } - width: units.iconSizes.desktop - height: width - animated: false - usesPlasmaTheme: false - source: icon - } - } - Column { - id: mainColumn - spacing: units.smallSpacing - - //This instance is purely for metrics - PlasmaExtras.Heading { - id: tooltipMaintextPlaceholder - visible: false - level: 3 - text: mainText - textFormat: Text.PlainText - } - PlasmaExtras.Heading { - id: tooltipMaintext - level: 3 - width: Math.min(tooltipMaintextPlaceholder.width, preferredTextWidth) - height: undefined // unset stupid PlasmaComponents.Label default height - //width: 400 - elide: Text.ElideRight - wrapMode: Text.WordWrap - // if there's no subtext allow two lines of window title - maximumLineCount: tooltipSubtext.visible ? 1 : 2 - lineHeight: 0.95 - text: mainText - textFormat: Text.PlainText - } - PlasmaComponents.Label { - id: tooltipSubtext - width: tooltipContentItem.preferredTextWidth - height: Math.min(theme.mSize(theme.defaultFont), contentHeight) - wrapMode: Text.WordWrap - text: subText - textFormat: Text.PlainText - opacity: 0.5 - visible: text !== "" + function generateSubText() { + var subTextEntries = new Array(); + + var vd = isGroup == true ? VirtualDesktop : parentTask.m.VirtualDesktop; + + if (!plasmoid.configuration.showOnlyCurrentDesktop + && virtualDesktopInfo.numberOfDesktops > 1 + && (isGroup == true ? IsOnAllVirtualDesktops : parentTask.m.IsOnAllVirtualDesktops) !== true + && vd != -1 + && vd != undefined + && virtualDesktopInfo.desktopNames[vd - 1] != undefined) { + subTextEntries.push(i18n("On %1", virtualDesktopInfo.desktopNames[vd - 1])); + } + + var act = isGroup == true ? Activities : parentTask.m.Activities; + 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 = new Array(); + + for (var i = 0; i < act.length; i++) { + var activity = act[i]; + + if (plasmoid.configuration.showOnlyCurrentActivity) { + if (activity != activityInfo.currentActivity) { + activityNames.push(activityInfo.activityName(act[i])); + } + } else if (activity != activityInfo.currentActivity) { + activityNames.push(activityInfo.activityName(act[i])); + } + } + + 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"); + } } } } diff --git a/applets/taskmanager/package/contents/ui/ToolTipWindowMouseArea.qml b/applets/taskmanager/package/contents/ui/ToolTipWindowMouseArea.qml --- a/applets/taskmanager/package/contents/ui/ToolTipWindowMouseArea.qml +++ b/applets/taskmanager/package/contents/ui/ToolTipWindowMouseArea.qml @@ -26,36 +26,22 @@ MouseArea { property var modelIndex property int winId // FIXME Legacy - property Item thumbnailItem + property Item rootTask - acceptedButtons: Qt.LeftButton + acceptedButtons: Qt.LeftButton | Qt.RightButton hoverEnabled: true enabled: winId != 0 onClicked: { - tasksModel.requestActivate(modelIndex); - toolTip.hideToolTip(); + if (mouse.button == Qt.LeftButton) { + tasksModel.requestActivate(modelIndex); + } else { + tasks.createContextMenu(rootTask, modelIndex).show(); + } + toolTipLoader.item.hideToolTip(); } onContainsMouseChanged: { tasks.windowsHovered([winId], containsMouse); } - - PlasmaComponents.ToolButton { - anchors { - top: parent.top - topMargin: thumbnailItem ? (thumbnailItem.height - thumbnailItem.paintedHeight) / 2 : 0 - right: parent.right - rightMargin: thumbnailItem ? (thumbnailItem.width - thumbnailItem.paintedWidth) / 2 : 0 - } - - iconSource: "window-close" - visible: parent.containsMouse && winId != 0 - tooltip: i18nc("close this window", "Close") - - onClicked: { - backend.cancelHighlightWindows(); - tasksModel.requestClose(modelIndex); - } - } } diff --git a/applets/taskmanager/package/contents/ui/main.qml b/applets/taskmanager/package/contents/ui/main.qml --- a/applets/taskmanager/package/contents/ui/main.qml +++ b/applets/taskmanager/package/contents/ui/main.qml @@ -170,7 +170,6 @@ id: backend taskManagerItem: groupDialog.visible ? null : tasks - toolTipItem: toolTipDelegate highlightWindows: plasmoid.configuration.highlightWindows onAddLauncher: { @@ -182,8 +181,7 @@ id: mpris2Source engine: "mpris2" connectedSources: sources - - function sourceNameForLauncherUrl(launcherUrl) { + function sourceNameForLauncherUrl(launcherUrl, pid) { if (!launcherUrl) { return ""; } @@ -192,12 +190,16 @@ // Moreover, remove URL parameters, like wmClass (part after the question mark) var desktopFileName = launcherUrl.toString().split('/').pop().split('?')[0].replace(".desktop", "") - for (var i = 0, length = sources.length; i < length; ++i) { - var source = sources[i]; + for (var i = 0, length = connectedSources.length; i < length; ++i) { + var source = connectedSources[i]; + // we intend to connect directly, otherwise the multiplexer steals the connection away + if (source === "@multiplex") { + continue; + } var sourceData = data[source]; - if (sourceData && sourceData.DesktopEntry === desktopFileName) { - return source + if (sourceData && sourceData.DesktopEntry === desktopFileName && sourceData.InstancePid === pid) { + return source; } } @@ -321,12 +323,6 @@ } } - ToolTipDelegate { - id: toolTipDelegate - - visible: false - } - TaskList { id: taskList @@ -370,10 +366,24 @@ id: taskRepeater delegate: Task {} - onItemAdded: taskList.layout() onItemRemoved: taskList.layout() + + // Workaround for tooltips submodel index. + // See toolTipLoader in Task.qml for further info. + signal reloadToolTips() + function delayedReloadToolTip() { + reloadTimer.restart(); + } + // + // + } + Timer { + id: reloadTimer + interval: 10 + onTriggered: taskRepeater.reloadToolTips() } + // } GroupDialog { id: groupDialog } @@ -404,9 +414,10 @@ dragSource = null; } - function createContextMenu(task) { - var menu = tasks.contextMenuComponent.createObject(task); - menu.visualParent = task; + function createContextMenu(rootTask, modelIndex) { + var menu = tasks.contextMenuComponent.createObject(rootTask); + menu.visualParent = rootTask; + menu.modelIndex = modelIndex; menu.mpris2Source = mpris2Source; return menu; }