mirror of
https://gitgud.io/wackyideas/aerothemeplasma.git
synced 2024-08-15 00:43:43 +00:00
1132 lines
47 KiB
QML
Executable file
1132 lines
47 KiB
QML
Executable file
/*
|
|
SPDX-FileCopyrightText: 2012-2016 Eike Hein <hein@kde.org>
|
|
SPDX-FileCopyrightText: 2016 Kai Uwe Broulik <kde@privat.broulik.de>
|
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later
|
|
*/
|
|
|
|
import QtQuick 2.15
|
|
import QtQuick.Layouts 1.1
|
|
import QtQuick.Controls 1.4
|
|
import org.kde.plasma.plasmoid 2.0
|
|
|
|
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
|
|
|
|
import "code/layout.js" as LayoutManager
|
|
import org.kde.plasma.extras 2.0 as PlasmaExtras
|
|
|
|
/*
|
|
* This is the custom context menu control for SevenTasks.
|
|
* It is designed to look and feel like the context menu from Windows Vista and onwards,
|
|
* while making sure that it behaves just like a normal context menu under KDE. This means:
|
|
*
|
|
* 1. The context menu grabs *all* mouse and key inputs.
|
|
* 2. The context menu must disappear if an event outside of it causes it to 'lose focus'.
|
|
* 3. The context menu must disappear if a menu item has been activated either with the mouse or the keyboard.
|
|
* 4. The context menu must disappear if the user clicks away from it or the Escape key is pressed on the keyboard.
|
|
*
|
|
* As PlasmaCore.Dialog inherits QWindow, we can use QWindow::setMouseGrabEnabled(bool) and QWindow::setKeyboardGrabEnabled(bool)
|
|
* to steal all mouse and keyboard events from the system and direct it towards the context menu. This is done through C++, more
|
|
* info in the C++ source files.
|
|
*
|
|
*/
|
|
|
|
PlasmaCore.Dialog {
|
|
id: tasksMenu
|
|
|
|
// Properties passed by the task when the context menu is created dynamically.
|
|
// Context menu specific stuff.
|
|
property QtObject backend
|
|
property QtObject mpris2Source
|
|
property var modelIndex
|
|
readonly property var atm: TaskManager.AbstractTasksModel
|
|
property var menuDecoration: "exec"
|
|
property QtObject currentItem: null
|
|
property int currentItemIndex: -1
|
|
|
|
readonly property int menuItemHeight: PlasmaCore.Units.smallSpacing*5
|
|
readonly property int menuWidth: 263
|
|
|
|
property bool showAllPlaces: false
|
|
property bool alsoCloseTask: false
|
|
property bool secondaryColumn: false
|
|
|
|
property color backgroundColorStatic: "#f1f6fb"
|
|
property color backgroundColorGradient: "white"
|
|
property color borderColor: "#ccd9ea"
|
|
|
|
|
|
// Functions inherited from the original ContextMenu
|
|
function get(modelProp) {
|
|
return tasksModel.data(modelIndex, modelProp)
|
|
}
|
|
function showContextMenuWithAllPlaces() {
|
|
visualParent.showContextMenu({showAllPlaces: true});
|
|
}
|
|
|
|
function newPlasmaMenuItem(parent) {
|
|
return Qt.createQmlObject(`
|
|
import org.kde.plasma.components 2.0 as PlasmaComponents
|
|
|
|
PlasmaComponents.MenuItem {}
|
|
`, parent);
|
|
}
|
|
|
|
function newPlasmaSeparator(parent) {
|
|
return Qt.createQmlObject(`
|
|
import org.kde.plasma.components 2.0 as PlasmaComponents
|
|
|
|
PlasmaComponents.MenuItem { separator: true }
|
|
`, parent);
|
|
}
|
|
function newMenuItem(parent) {
|
|
return Qt.createQmlObject(`
|
|
TasksMenuItemWrapper {}
|
|
`, parent);
|
|
}
|
|
|
|
function newSeparator(parent) {
|
|
return Qt.createQmlObject(`
|
|
TasksMenuItemSeparator {}
|
|
`, parent);
|
|
}
|
|
function addItemToMenu(obj) {
|
|
obj.Layout.fillWidth = true;
|
|
obj.Layout.preferredHeight = menuItemHeight;
|
|
menuitems.height += obj.Layout.preferredHeight + menuitems.spacing;
|
|
}
|
|
function clearIndices() {
|
|
if(currentItem !== null) {
|
|
currentItem.selected = false;
|
|
currentItem = null;
|
|
}
|
|
currentItemIndex = -1;
|
|
}
|
|
function setCurrentItem(obj) {
|
|
clearIndices();
|
|
var i = Array.prototype.indexOf.call(menuitems.children, obj);
|
|
if(i === -1) {
|
|
i = menuitems.children.length + Array.prototype.indexOf.call(staticMenuItems.children, obj);
|
|
}
|
|
currentItemIndex = i;
|
|
currentItem = obj;
|
|
currentItem.selected = true;
|
|
}
|
|
|
|
// Tasksmenu specific stuff
|
|
property alias tMenu: tasksMenu
|
|
property int xpos: -1 // Variable is used to keep track of the original x position which sometimes gets changed for no reason.
|
|
visible: false
|
|
opacity: 0
|
|
objectName: "tasksMenuDialog"
|
|
hideOnWindowDeactivate: true // Makes it so that the context menu disappears if it gets forcibly out of focus by an external event.
|
|
flags: Qt.WindowStaysOnTopHint | Qt.Dialog
|
|
|
|
// Used to animate the context menu appearing and disappearing.
|
|
Behavior on opacity {
|
|
NumberAnimation { duration: 125; }
|
|
}
|
|
Behavior on y {
|
|
NumberAnimation {duration: 125; }
|
|
}
|
|
|
|
// Tries to detect when the x position resets to 0.
|
|
onXChanged: {
|
|
if(tasksMenu.x !== xpos) {
|
|
tasksMenu.x = xpos;
|
|
}
|
|
}
|
|
// If the context menu is no longer visible (most often when it loses focus), close the menu.
|
|
onVisibleChanged: {
|
|
if(visible) {
|
|
var diff = parent.mapToGlobal(tasksMenu.x, tasksMenu.y).x - tasksMenu.x;
|
|
xpos = visualParent.x + diff + visualParent.width/2 - PlasmaCore.Units.smallSpacing;
|
|
xpos -= menuWidth / 2;
|
|
|
|
if(xpos < 0) xpos = 0;
|
|
tasksMenu.x = xpos;
|
|
plasmoid.nativeInterface.setMouseGrab(true, tasksMenu);
|
|
}
|
|
else if(!visible) {
|
|
tasksMenu.closeMenu();
|
|
}
|
|
}
|
|
onActiveChanged: {
|
|
if(!active) tasksMenu.close();
|
|
}
|
|
// Set to Floating so that the borders are visible all the time, even when it is right next to another object.
|
|
location: {
|
|
return PlasmaCore.Types.Floating;
|
|
}
|
|
// Used publicly by other objects to show the dynamically created context menu.
|
|
function show() {
|
|
loadDynamicLauncherActions(get(atm.LauncherUrlWithoutIcon));
|
|
visible = true;
|
|
opacity = 1;
|
|
tasksMenu.y -= PlasmaCore.Units.smallSpacing*2;
|
|
openTimer.start();
|
|
}
|
|
// Closes the menu gracefully, by first showing a fade out animation before freeing the object from memory.
|
|
function closeMenu() {
|
|
plasmoid.nativeInterface.disableBlurBehind(tasksMenu);
|
|
tasksMenu.y += PlasmaCore.Units.smallSpacing*2;
|
|
opacity = 0;
|
|
closeTimer.start();
|
|
}
|
|
|
|
function loadDynamicLauncherActions(launcherUrl) {
|
|
var sections = [
|
|
{
|
|
title: i18n("Frequent"),
|
|
group: "places",
|
|
actions: backend.placesActions(launcherUrl, showAllPlaces, tasksMenu)
|
|
},
|
|
{
|
|
title: i18n("Recent"),
|
|
group: "recents",
|
|
actions: backend.recentDocumentActions(launcherUrl, tasksMenu)
|
|
},
|
|
{
|
|
title: i18n("Tasks"),
|
|
group: "actions",
|
|
actions: backend.jumpListActions(launcherUrl, tasksMenu)
|
|
}
|
|
]
|
|
|
|
// C++ can override section heading by returning a QString as first action
|
|
sections.forEach((section) => {
|
|
if (typeof section.actions[0] === "string") {
|
|
section.title = section.actions.shift(); // take first
|
|
}
|
|
});
|
|
|
|
// QMenu does not limit its width automatically. Even if we set a maximumWidth
|
|
// it would just cut off text rather than eliding. So we do this manually.
|
|
var textMetrics = Qt.createQmlObject("import QtQuick 2.4; TextMetrics {}", menuitems);
|
|
var maximumWidth = LayoutManager.maximumContextMenuTextWidth() + PlasmaCore.Units.smallSpacing*2;
|
|
|
|
for(var i = 0; i < sections.length; i++) {
|
|
var section = sections[i];
|
|
if(section["actions"].length == 0) continue;
|
|
|
|
// Make a separator header
|
|
var sepHeader = tasksMenu.newSeparator(menuitems);
|
|
sepHeader.menuText = section["title"];
|
|
addItemToMenu(sepHeader);
|
|
|
|
for(var j = 0; j < section["actions"].length; j++) {
|
|
if(section["group"] == "recents" && j == section["actions"].length-2) continue;
|
|
var mAction = section["actions"][j];
|
|
var mItem = tasksMenu.newMenuItem(menuitems);
|
|
// Crude way of manually eliding...
|
|
var elided = false;
|
|
textMetrics.text = Qt.binding(function() {
|
|
return mAction.text;
|
|
});
|
|
|
|
while (textMetrics.width > maximumWidth) {
|
|
mAction.text = mAction.text.slice(0, -1);
|
|
elided = true;
|
|
}
|
|
|
|
if (elided) {
|
|
mAction.text += "…";
|
|
}
|
|
mItem.text = mAction.text;
|
|
mItem.icon = mAction.icon;
|
|
mItem.clicked.connect(mAction.trigger);
|
|
addItemToMenu(mItem);
|
|
secondaryColumn = true;
|
|
}
|
|
|
|
}
|
|
|
|
// Add Media Player control actions
|
|
var sourceName = mpris2Source.sourceNameForLauncherUrl(launcherUrl, get(atm.AppPid));
|
|
|
|
if (sourceName && !(get(atm.WinIdList) !== undefined && get(atm.WinIdList).length > 1)) {
|
|
var playerData = mpris2Source.data[sourceName]
|
|
var sepHeader = tasksMenu.newSeparator(menuitems);
|
|
sepHeader.menuText = "Media controls";
|
|
addItemToMenu(sepHeader);
|
|
secondaryColumn = true;
|
|
if (playerData.CanControl) {
|
|
var playing = (playerData.PlaybackStatus === "Playing");
|
|
var menuItem = tasksMenu.newMenuItem(menuitems);
|
|
menuItem.text = i18nc("Play previous track", "Previous Track");
|
|
menuItem.icon = "media-skip-backward";
|
|
menuItem.enabled = Qt.binding(function() {
|
|
return playerData.CanGoPrevious;
|
|
});
|
|
menuItem.clicked.connect(function() {
|
|
mpris2Source.goPrevious(sourceName);
|
|
tasksMenu.closeMenu();
|
|
});
|
|
tasksMenu.addItemToMenu(menuItem);
|
|
|
|
menuItem = tasksMenu.newMenuItem(menuitems);
|
|
// PlasmaCore Menu doesn't actually handle icons or labels changing at runtime...
|
|
menuItem.text = Qt.binding(function() {
|
|
// if CanPause, toggle the menu entry between Play & Pause, otherwise always use Play
|
|
return playing && playerData.CanPause ? i18nc("Pause playback", "Pause") : i18nc("Start playback", "Play");
|
|
});
|
|
menuItem.icon = Qt.binding(function() {
|
|
return playing && playerData.CanPause ? "media-playback-pause" : "media-playback-start";
|
|
});
|
|
menuItem.enabled = Qt.binding(function() {
|
|
return playing ? playerData.CanPause : playerData.CanPlay;
|
|
});
|
|
menuItem.clicked.connect(function() {
|
|
if (playing) {
|
|
mpris2Source.pause(sourceName);
|
|
} else {
|
|
mpris2Source.play(sourceName);
|
|
}
|
|
tasksMenu.closeMenu();
|
|
});
|
|
tasksMenu.addItemToMenu(menuItem);
|
|
|
|
menuItem = tasksMenu.newMenuItem(menuitems);
|
|
menuItem.text = i18nc("Play next track", "Next Track");
|
|
menuItem.icon = "media-skip-forward";
|
|
menuItem.enabled = Qt.binding(function() {
|
|
return playerData.CanGoNext;
|
|
});
|
|
menuItem.clicked.connect(function() {
|
|
mpris2Source.goNext(sourceName);
|
|
tasksMenu.closeMenu();
|
|
});
|
|
tasksMenu.addItemToMenu(menuItem);
|
|
|
|
menuItem = tasksMenu.newMenuItem(menuitems);
|
|
menuItem.text = i18nc("Stop playback", "Stop");
|
|
menuItem.icon = "media-playback-stop";
|
|
menuItem.enabled = Qt.binding(function() {
|
|
return playerData.PlaybackStatus !== "Stopped";
|
|
});
|
|
menuItem.clicked.connect(function() {
|
|
mpris2Source.stop(sourceName);
|
|
tasksMenu.closeMenu();
|
|
});
|
|
tasksMenu.addItemToMenu(menuItem);
|
|
|
|
// If we don't have a window associated with the player but we can quit
|
|
// it through MPRIS we'll offer a "Quit" option instead of "Close"
|
|
if (!closeWindowItem.visible && playerData.CanQuit) {
|
|
menuItem = tasksMenu.newMenuItem(menuitems);
|
|
menuItem.text = i18nc("Quit media player app", "Quit");
|
|
menuItem.icon = "application-exit";
|
|
menuItem.visible = Qt.binding(function() {
|
|
return !closeWindowItem.visible;
|
|
});
|
|
menuItem.clicked.connect(function() {
|
|
mpris2Source.quit(sourceName);
|
|
tasksMenu.closeMenu();
|
|
});
|
|
tasksMenu.addItemToMenu(menuItem);
|
|
}
|
|
|
|
// If we don't have a window associated with the player but we can raise
|
|
// it through MPRIS we'll offer a "Restore" option
|
|
if (get(atm.IsLauncher) === true && !startNewInstanceItem.visible && playerData.CanRaise) {
|
|
menuItem = tasksMenu.newMenuItem(menuitems);
|
|
menuItem.text = i18nc("Open or bring to the front window of media player app", "Restore");
|
|
menuItem.icon = playerData["Desktop Icon Name"];
|
|
menuItem.visible = Qt.binding(function() {
|
|
return !startNewInstanceItem.visible;
|
|
});
|
|
menuItem.clicked.connect(function() {
|
|
mpris2Source.raise(sourceName);
|
|
tasksMenu.closeMenu();
|
|
});
|
|
tasksMenu.addItemToMenu(menuItem);
|
|
}
|
|
}
|
|
}
|
|
|
|
// We allow mute/unmute whenever an application has a stream, regardless of whether it
|
|
// is actually playing sound.
|
|
// This way you can unmute, e.g. a telephony app, even after the conversation has ended,
|
|
// so you still have it ringing later on.
|
|
if (tasksMenu.visualParent.hasAudioStream) {
|
|
var muteItem = tasksMenu.newMenuItem(menuitems);
|
|
muteItem.checkable = true;
|
|
muteItem.checked = Qt.binding(function() {
|
|
return tasksMenu.visualParent && tasksMenu.visualParent.muted;
|
|
});
|
|
muteItem.clicked.connect(function() {
|
|
tasksMenu.visualParent.toggleMuted();
|
|
muteItem.text = !muteItem.checked ? "Unmute" : "Mute";
|
|
muteItem.icon = !muteItem.checked ? "audio-volume-muted" : "audio-volume-high";
|
|
});
|
|
muteItem.text = muteItem.checked ? "Unmute" : "Mute";
|
|
muteItem.icon = muteItem.checked ? "audio-volume-muted" : "audio-volume-high";
|
|
tasksMenu.addItemToMenu(muteItem);
|
|
secondaryColumn = true;
|
|
|
|
}
|
|
}
|
|
|
|
function delayedMenu(delay, func) {
|
|
plasmoid.nativeInterface.disableBlurBehind(tasksMenu);
|
|
tasksMenu.y += PlasmaCore.Units.smallSpacing*2;
|
|
opacity = 0;
|
|
delayTimer.interval = delay;
|
|
delayTimer.repeat = false;
|
|
delayTimer.triggered.connect(func);
|
|
delayTimer.start();
|
|
}
|
|
|
|
FocusScope {
|
|
id: fscope
|
|
focus: true
|
|
Layout.minimumWidth: menuWidth
|
|
Layout.maximumWidth: menuWidth
|
|
Layout.minimumHeight: staticMenuItems.height + menuitems.height + PlasmaCore.Units.smallSpacing*3 - (secondaryColumn ? 0 : PlasmaCore.Units.smallSpacing*2)
|
|
Layout.maximumHeight: staticMenuItems.height + menuitems.height + PlasmaCore.Units.smallSpacing*3 - (secondaryColumn ? 0 : PlasmaCore.Units.smallSpacing*2)
|
|
// This is the last resort to avoiding the dialog displacement bug. It's set to correct the x position at a delay of 18ms.
|
|
// This may result in a brief but noticeable jump in position when the context menu is shown.
|
|
Timer {
|
|
id: delayTimer
|
|
}
|
|
Timer {
|
|
id: openTimer
|
|
interval: 20
|
|
repeat: false
|
|
onTriggered: {
|
|
tasksMenu.x = xpos;
|
|
plasmoid.nativeInterface.setMouseGrab(true, tasksMenu);
|
|
}
|
|
}
|
|
// Timer used to free the object from memory after the fade out animation has finished.
|
|
Timer {
|
|
id: closeTimer
|
|
interval: 150
|
|
onTriggered: {
|
|
tasksMenu.destroy();
|
|
}
|
|
}
|
|
ColumnLayout {
|
|
id: menuitems
|
|
z: 1
|
|
spacing: PlasmaCore.Units.smallSpacing/2
|
|
anchors {
|
|
left: parent.left
|
|
right: parent.right
|
|
bottom: staticMenuItems.top
|
|
leftMargin: PlasmaCore.Units.smallSpacing*2
|
|
rightMargin: PlasmaCore.Units.smallSpacing*2
|
|
bottomMargin: PlasmaCore.Units.smallSpacing
|
|
}
|
|
|
|
Item {
|
|
Layout.fillHeight: true
|
|
}
|
|
Item {
|
|
height: PlasmaCore.Units.smallSpacing
|
|
}
|
|
}
|
|
ColumnLayout {
|
|
id: staticMenuItems
|
|
z: 1
|
|
spacing: PlasmaCore.Units.smallSpacing/2
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
anchors.bottom: parent.bottom
|
|
anchors.margins: PlasmaCore.Units.smallSpacing*2
|
|
|
|
Item {
|
|
Layout.fillHeight: true
|
|
}
|
|
Item {
|
|
height: PlasmaCore.Units.smallSpacing
|
|
}
|
|
|
|
TasksMenuItemWrapper {
|
|
id: startNewInstanceItem
|
|
visible: get(atm.CanLaunchNewInstance)
|
|
text: get(atm.AppName)
|
|
icon: menuDecoration
|
|
onClicked: tasksModel.requestNewInstance(modelIndex)
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: menuItemHeight
|
|
}
|
|
TasksMenuItemWrapper {
|
|
id: virtualDesktopsMenuItem
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: menuItemHeight
|
|
text: i18n("Move to Desktop...")
|
|
icon: "virtual-desktops"
|
|
|
|
visible: virtualDesktopInfo.numberOfDesktops > 1 && (visualParent && get(atm.IsLauncher) !== true && get(atm.IsStartup) !== true && get(atm.IsVirtualDesktopsChangeable) === true)
|
|
|
|
onClicked: virtualDesktopsMenu.openRelative()
|
|
|
|
Connections {
|
|
target: virtualDesktopInfo
|
|
|
|
function onNumberOfDesktopsChanged() {Qt.callLater(virtualDesktopsMenu.refresh)}
|
|
function onDesktopIdsChanged() {Qt.callLater(virtualDesktopsMenu.refresh)}
|
|
function onDesktopNamesChanged() {Qt.callLater(virtualDesktopsMenu.refresh)}
|
|
}
|
|
PlasmaComponents.ContextMenu {
|
|
id: virtualDesktopsMenu
|
|
|
|
visualParent: virtualDesktopsMenuItem
|
|
onTriggered: {
|
|
tasksMenu.closeMenu();
|
|
}
|
|
|
|
function refresh() {
|
|
clearMenuItems();
|
|
|
|
if (virtualDesktopInfo.numberOfDesktops <= 1) {
|
|
return;
|
|
}
|
|
|
|
var menuItem = tasksMenu.newPlasmaMenuItem(virtualDesktopsMenu);
|
|
menuItem.text = i18n("Move &To Current Desktop");
|
|
menuItem.enabled = Qt.binding(function() {
|
|
return tasksMenu.visualParent && tasksMenu.get(atm.VirtualDesktops).indexOf(virtualDesktopInfo.currentDesktop) === -1;
|
|
});
|
|
menuItem.clicked.connect(function() {
|
|
tasksModel.requestVirtualDesktops(tasksMenu.modelIndex, [virtualDesktopInfo.currentDesktop]);
|
|
});
|
|
|
|
menuItem = tasksMenu.newPlasmaMenuItem(virtualDesktopsMenu);
|
|
menuItem.text = i18n("&All Desktops");
|
|
menuItem.checkable = true;
|
|
menuItem.checked = Qt.binding(function() {
|
|
return tasksMenu.visualParent && tasksMenu.get(atm.IsOnAllVirtualDesktops) === true;
|
|
});
|
|
menuItem.clicked.connect(function() {
|
|
tasksModel.requestVirtualDesktops(tasksMenu.modelIndex, []);
|
|
});
|
|
backend.setActionGroup(menuItem.action);
|
|
|
|
tasksMenu.newPlasmaSeparator(virtualDesktopsMenu);
|
|
|
|
for (var i = 0; i < virtualDesktopInfo.desktopNames.length; ++i) {
|
|
menuItem = tasksMenu.newPlasmaMenuItem(virtualDesktopsMenu);
|
|
menuItem.text = i18nc("1 = number of desktop, 2 = desktop name", "&%1 %2", i + 1, virtualDesktopInfo.desktopNames[i]);
|
|
menuItem.checkable = true;
|
|
menuItem.checked = Qt.binding((function(i) {
|
|
return function() { return tasksMenu.visualParent && tasksMenu.get(atm.VirtualDesktops).indexOf(virtualDesktopInfo.desktopIds[i]) > -1 };
|
|
})(i));
|
|
menuItem.clicked.connect((function(i) {
|
|
return function() { return tasksModel.requestVirtualDesktops(tasksMenu.modelIndex, [virtualDesktopInfo.desktopIds[i]]); };
|
|
})(i));
|
|
backend.setActionGroup(menuItem.action);
|
|
}
|
|
|
|
tasksMenu.newPlasmaSeparator(virtualDesktopsMenu);
|
|
|
|
menuItem = tasksMenu.newPlasmaMenuItem(virtualDesktopsMenu);
|
|
menuItem.text = i18n("&New Desktop");
|
|
menuItem.clicked.connect(function() {
|
|
tasksModel.requestNewVirtualDesktop(tasksMenu.modelIndex);
|
|
});
|
|
}
|
|
|
|
// Return mouse grabbing to the original context menu when the context menu closes
|
|
onStatusChanged: {
|
|
if(status == 3) {
|
|
plasmoid.nativeInterface.setMouseGrab(true, tasksMenu);
|
|
}
|
|
}
|
|
Component.onCompleted: {
|
|
if(virtualDesktopsMenuItem.visible) refresh()
|
|
}
|
|
}
|
|
}
|
|
|
|
TasksMenuItemWrapper {
|
|
id: activitiesDesktopsMenuItem
|
|
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: menuItemHeight
|
|
visible: activityInfo.numberOfRunningActivities > 1
|
|
&& (visualParent && !get(atm.IsLauncher)
|
|
&& !get(atm.IsStartup))
|
|
|
|
enabled: visible
|
|
|
|
text: i18n("Show in Activities...")
|
|
icon: "activities"
|
|
onClicked: activitiesDesktopsMenu.openRelative()
|
|
|
|
Connections {
|
|
target: activityInfo
|
|
|
|
function onNumberOfRunningActivitiesChanged() {
|
|
activitiesDesktopsMenu.refresh()
|
|
}
|
|
}
|
|
PlasmaComponents.ContextMenu {
|
|
id: activitiesDesktopsMenu
|
|
|
|
visualParent: activitiesDesktopsMenuItem
|
|
|
|
onTriggered: {
|
|
tasksMenu.closeMenu();
|
|
}
|
|
function refresh() {
|
|
clearMenuItems();
|
|
|
|
if (activityInfo.numberOfRunningActivities <= 1) {
|
|
return;
|
|
}
|
|
|
|
var menuItem = tasksMenu.newPlasmaMenuItem(activitiesDesktopsMenu);
|
|
menuItem.text = i18n("Add To Current Activity");
|
|
menuItem.enabled = Qt.binding(function() {
|
|
return tasksMenu.visualParent && tasksMenu.get(atm.Activities).length > 0 &&
|
|
tasksMenu.get(atm.Activities).indexOf(activityInfo.currentActivity) < 0;
|
|
});
|
|
menuItem.clicked.connect(function() {
|
|
tasksModel.requestActivities(tasksMenu.modelIndex, tasksMenu.get(atm.Activities).concat(activityInfo.currentActivity));
|
|
});
|
|
|
|
menuItem = tasksMenu.newPlasmaMenuItem(activitiesDesktopsMenu);
|
|
menuItem.text = i18n("All Activities");
|
|
menuItem.checkable = true;
|
|
menuItem.checked = Qt.binding(function() {
|
|
return tasksMenu.visualParent && tasksMenu.get(atm.Activities).length === 0;
|
|
});
|
|
menuItem.toggled.connect(function(checked) {
|
|
let newActivities = []; // will cast to an empty QStringList i.e all activities
|
|
if (!checked) {
|
|
newActivities = new Array(activityInfo.currentActivity);
|
|
}
|
|
tasksModel.requestActivities(tasksMenu.modelIndex, newActivities);
|
|
});
|
|
|
|
tasksMenu.newPlasmaSeparator(activitiesDesktopsMenu);
|
|
|
|
var runningActivities = activityInfo.runningActivities();
|
|
for (var i = 0; i < runningActivities.length; ++i) {
|
|
var activityId = runningActivities[i];
|
|
|
|
menuItem = tasksMenu.newPlasmaMenuItem(activitiesDesktopsMenu);
|
|
menuItem.text = activityInfo.activityName(runningActivities[i]);
|
|
menuItem.checkable = true;
|
|
menuItem.checked = Qt.binding( (function(activityId) {
|
|
return function() {
|
|
return tasksMenu.visualParent && tasksMenu.get(atm.Activities).indexOf(activityId) >= 0;
|
|
};
|
|
})(activityId));
|
|
menuItem.toggled.connect((function(activityId) {
|
|
return function (checked) {
|
|
var newActivities = tasksMenu.get(atm.Activities);
|
|
if (checked) {
|
|
newActivities = newActivities.concat(activityId);
|
|
} else {
|
|
var index = newActivities.indexOf(activityId)
|
|
if (index < 0) {
|
|
return;
|
|
}
|
|
|
|
newActivities.splice(index, 1);
|
|
}
|
|
return tasksModel.requestActivities(tasksMenu.modelIndex, newActivities);
|
|
};
|
|
})(activityId));
|
|
}
|
|
|
|
tasksMenu.newPlasmaSeparator(activitiesDesktopsMenu);
|
|
|
|
for (var i = 0; i < runningActivities.length; ++i) {
|
|
var activityId = runningActivities[i];
|
|
var onActivities = tasksMenu.get(atm.Activities);
|
|
|
|
// if the task is on a single activity, don't insert a "move to" item for that activity
|
|
if(onActivities.length == 1 && onActivities[0] == activityId) {
|
|
continue;
|
|
}
|
|
|
|
menuItem = tasksMenu.newPlasmaMenuItem(activitiesDesktopsMenu);
|
|
menuItem.text = i18n("Move to %1", activityInfo.activityName(activityId))
|
|
menuItem.clicked.connect((function(activityId) {
|
|
return function () {
|
|
return tasksModel.requestActivities(tasksMenu.modelIndex, [activityId]);
|
|
};
|
|
})(activityId));
|
|
}
|
|
|
|
tasksMenu.newPlasmaSeparator(activitiesDesktopsMenu);
|
|
}
|
|
|
|
onStatusChanged: {
|
|
if(status == 3) {
|
|
plasmoid.nativeInterface.setMouseGrab(true, tasksMenu);
|
|
}
|
|
}
|
|
Component.onCompleted: {
|
|
if(activitiesDesktopsMenuItem.visible) refresh()
|
|
}
|
|
}
|
|
}
|
|
|
|
TasksMenuItemWrapper {
|
|
id: launcherToggleAction
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: menuItemHeight
|
|
text: i18n("Pin this program to taskbar")
|
|
icon: "window-pin"
|
|
visible: visualParent
|
|
&& get(atm.IsLauncher) !== true
|
|
&& get(atm.IsStartup) !== true
|
|
&& plasmoid.immutability !== PlasmaCore.Types.SystemImmutable
|
|
&& (activityInfo.numberOfRunningActivities < 2)
|
|
&& !doesBelongToCurrentActivity()
|
|
|
|
function doesBelongToCurrentActivity() {
|
|
return tasksModel.launcherActivities(get(atm.LauncherUrlWithoutIcon)).some(function(activity) {
|
|
return activity === activityInfo.currentActivity || activity === activityInfo.nullUuid;
|
|
});
|
|
}
|
|
|
|
onClicked: {
|
|
tasksModel.requestAddLauncher(get(atm.LauncherUrl));
|
|
tasksMenu.closeMenu();
|
|
}
|
|
}
|
|
TasksMenuItemWrapper {
|
|
id: showLauncherInActivitiesItem
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: menuItemHeight
|
|
text: i18n("Pin this program to taskbar") + "..."
|
|
icon: "window-pin"
|
|
|
|
visible: visualParent
|
|
&& get(atm.IsStartup) !== true
|
|
&& plasmoid.immutability !== PlasmaCore.Types.SystemImmutable
|
|
&& (activityInfo.numberOfRunningActivities >= 2)
|
|
|
|
onClicked: activitiesLaunchersMenu.openRelative()
|
|
Connections {
|
|
target: activityInfo
|
|
function onNumberOfRunningActivitiesChanged() {
|
|
activitiesDesktopsMenu.refresh()
|
|
}
|
|
}
|
|
|
|
PlasmaComponents.ContextMenu {
|
|
id: activitiesLaunchersMenu
|
|
visualParent: showLauncherInActivitiesItem
|
|
|
|
function refresh() {
|
|
clearMenuItems();
|
|
if (tasksMenu.visualParent === null) return;
|
|
|
|
var createNewItem = function(id, title, url, activities) {
|
|
var result = tasksMenu.newPlasmaMenuItem(activitiesLaunchersMenu);
|
|
result.text = title;
|
|
|
|
result.visible = true;
|
|
result.checkable = true;
|
|
|
|
result.checked = activities.some(function(activity) { return activity === id });
|
|
|
|
result.clicked.connect(
|
|
function() {
|
|
delayedMenu(150, function() {
|
|
if (result.checked) {
|
|
tasksModel.requestAddLauncherToActivity(url, id);
|
|
} else {
|
|
tasksModel.requestRemoveLauncherFromActivity(url, id);
|
|
}
|
|
tasksMenu.destroy();
|
|
});
|
|
}
|
|
);
|
|
|
|
return result;
|
|
}
|
|
|
|
if (tasksMenu.visualParent === null) return;
|
|
var url = tasksMenu.get(atm.LauncherUrlWithoutIcon);
|
|
var activities = tasksModel.launcherActivities(url);
|
|
createNewItem(activityInfo.nullUuid, i18n("On All Activities"), url, activities);
|
|
|
|
if (activityInfo.numberOfRunningActivities <= 1) {
|
|
return;
|
|
}
|
|
|
|
createNewItem(activityInfo.currentActivity, i18n("On The Current Activity"), url, activities);
|
|
tasksMenu.newPlasmaSeparator(activitiesLaunchersMenu);
|
|
var runningActivities = activityInfo.runningActivities();
|
|
runningActivities.forEach(function(id) {
|
|
createNewItem(id, activityInfo.activityName(id), url, activities);
|
|
});
|
|
}
|
|
|
|
onStatusChanged: {
|
|
if(status == 3) {
|
|
plasmoid.nativeInterface.setMouseGrab(true, tasksMenu);
|
|
}
|
|
}
|
|
Component.onCompleted: {
|
|
tasksMenu.onVisualParentChanged.connect(refresh);
|
|
refresh();
|
|
}
|
|
}
|
|
|
|
}
|
|
TasksMenuItemWrapper {
|
|
id: unpinFromTaskMan
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: menuItemHeight
|
|
|
|
visible: (visualParent
|
|
&& plasmoid.immutability !== PlasmaCore.Types.SystemImmutable
|
|
&& !launcherToggleAction.visible
|
|
&& activityInfo.numberOfRunningActivities < 2)
|
|
|
|
text: i18n("Unpin this program from taskbar")
|
|
icon: "window-unpin"
|
|
onClicked: {
|
|
delayedMenu(150, function() {
|
|
tasksModel.requestRemoveLauncher(get(atm.LauncherUrlWithoutIcon));
|
|
tasksMenu.destroy();
|
|
});
|
|
}
|
|
|
|
}
|
|
TasksMenuItemWrapper {
|
|
id: moreActionsMenuItem
|
|
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: menuItemHeight
|
|
visible: (visualParent && get(atm.IsLauncher) !== true && get(atm.IsStartup) !== true)
|
|
|
|
enabled: visible
|
|
|
|
text: i18n("More") + "..."
|
|
icon: "view-more-symbolic"
|
|
|
|
onClicked: moreActionsMenu.openRelative();
|
|
|
|
PlasmaComponents.ContextMenu {
|
|
id: moreActionsMenu
|
|
visualParent: moreActionsMenuItem
|
|
|
|
onTriggered: {
|
|
plasmoid.nativeInterface.setMouseGrab(false, tasksMenu);
|
|
tasksMenu.closeMenu();
|
|
}
|
|
onStatusChanged: {
|
|
if(status == 3) {
|
|
plasmoid.nativeInterface.setMouseGrab(true, tasksMenu);
|
|
}
|
|
}
|
|
|
|
PlasmaComponents.MenuItem {
|
|
enabled: tasksMenu.visualParent && tasksMenu.get(atm.IsMovable) === true
|
|
|
|
text: i18n("&Move")
|
|
icon: "transform-move"
|
|
|
|
onClicked: tasksModel.requestMove(tasksMenu.modelIndex)
|
|
}
|
|
|
|
PlasmaComponents.MenuItem {
|
|
enabled: tasksMenu.visualParent && tasksMenu.get(atm.IsResizable) === true
|
|
|
|
text: i18n("Re&size")
|
|
icon: "transform-scale"
|
|
|
|
onClicked: tasksModel.requestResize(tasksMenu.modelIndex)
|
|
}
|
|
|
|
PlasmaComponents.MenuItem {
|
|
visible: (tasksMenu.visualParent && get(atm.IsLauncher) !== true && get(atm.IsStartup) !== true)
|
|
|
|
enabled: tasksMenu.visualParent && get(atm.IsMaximizable) === true
|
|
|
|
checkable: true
|
|
checked: tasksMenu.visualParent && get(atm.IsMaximized) === true
|
|
|
|
text: i18n("Ma&ximize")
|
|
icon: "window-maximize"
|
|
|
|
onClicked: tasksModel.requestToggleMaximized(modelIndex)
|
|
}
|
|
|
|
PlasmaComponents.MenuItem {
|
|
visible: (tasksMenu.visualParent && get(atm.IsLauncher) !== true && get(atm.IsStartup) !== true)
|
|
|
|
enabled: tasksMenu.visualParent && get(atm.IsMinimizable) === true
|
|
|
|
checkable: true
|
|
checked: tasksMenu.visualParent && get(atm.IsMinimized) === true
|
|
|
|
text: i18n("Mi&nimize")
|
|
icon: "window-minimize"
|
|
|
|
onClicked: tasksModel.requestToggleMinimized(modelIndex)
|
|
}
|
|
|
|
PlasmaComponents.MenuItem {
|
|
checkable: true
|
|
checked: tasksMenu.visualParent && tasksMenu.get(atm.IsKeepAbove) === true
|
|
|
|
text: i18n("Keep &Above Others")
|
|
icon: "window-keep-above"
|
|
|
|
onClicked: tasksModel.requestToggleKeepAbove(tasksMenu.modelIndex)
|
|
}
|
|
|
|
PlasmaComponents.MenuItem {
|
|
checkable: true
|
|
checked: tasksMenu.visualParent && tasksMenu.get(atm.IsKeepBelow) === true
|
|
|
|
text: i18n("Keep &Below Others")
|
|
icon: "window-keep-below"
|
|
|
|
onClicked: tasksModel.requestToggleKeepBelow(tasksMenu.modelIndex)
|
|
}
|
|
|
|
PlasmaComponents.MenuItem {
|
|
enabled: tasksMenu.visualParent && tasksMenu.get(atm.IsFullScreenable) === true
|
|
|
|
checkable: true
|
|
checked: tasksMenu.visualParent && tasksMenu.get(atm.IsFullScreen) === true
|
|
|
|
text: i18n("&Fullscreen")
|
|
icon: "view-fullscreen"
|
|
|
|
onClicked: tasksModel.requestToggleFullScreen(tasksMenu.modelIndex)
|
|
}
|
|
|
|
PlasmaComponents.MenuItem {
|
|
enabled: tasksMenu.visualParent && tasksMenu.get(atm.IsShadeable) === true
|
|
|
|
checkable: true
|
|
checked: tasksMenu.visualParent && tasksMenu.get(atm.IsShaded) === true
|
|
|
|
text: i18n("&Shade")
|
|
icon: "window-shade"
|
|
|
|
onClicked: tasksModel.requestToggleShaded(tasksMenu.modelIndex)
|
|
}
|
|
|
|
PlasmaComponents.MenuItem {
|
|
separator: true
|
|
}
|
|
|
|
PlasmaComponents.MenuItem {
|
|
visible: (plasmoid.configuration.groupingStrategy !== 0) && tasksMenu.get(atm.IsWindow) === true
|
|
|
|
checkable: true
|
|
checked: tasksMenu.visualParent && tasksMenu.get(atm.IsGroupable) === true
|
|
|
|
text: i18n("Allow this program to be grouped")
|
|
icon: "view-group"
|
|
|
|
onClicked: tasksModel.requestToggleGrouping(menu.modelIndex)
|
|
}
|
|
|
|
PlasmaComponents.MenuItem {
|
|
separator: true
|
|
}
|
|
|
|
PlasmaComponents.MenuItem {
|
|
property QtObject configureAction: null
|
|
|
|
enabled: configureAction && configureAction.enabled
|
|
visible: configureAction && configureAction.visible
|
|
|
|
text: configureAction ? configureAction.text : ""
|
|
icon: configureAction ? configureAction.icon : ""
|
|
|
|
onClicked: configureAction.trigger()
|
|
|
|
Component.onCompleted: configureAction = plasmoid.action("configure")
|
|
}
|
|
|
|
PlasmaComponents.MenuItem {
|
|
property QtObject alternativesAction: null
|
|
|
|
enabled: alternativesAction && alternativesAction.enabled
|
|
visible: alternativesAction && alternativesAction.visible
|
|
|
|
text: alternativesAction ? alternativesAction.text : ""
|
|
icon: alternativesAction ? alternativesAction.icon : ""
|
|
|
|
onClicked: alternativesAction.trigger()
|
|
|
|
Component.onCompleted: alternativesAction = plasmoid.action("alternatives")
|
|
}
|
|
}
|
|
}
|
|
|
|
TasksMenuItemWrapper {
|
|
id: closeWindowItem
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: menuItemHeight
|
|
|
|
visible: (visualParent && get(atm.IsLauncher) !== true && get(atm.IsStartup) !== true)
|
|
|
|
enabled: visualParent && get(atm.IsClosable) === true
|
|
|
|
text: get(atm.IsGroupParent) ? "Close all windows" : i18n("Close window")
|
|
icon: "window-close"
|
|
onClicked: {
|
|
alsoCloseTask = true;
|
|
closeMenu();
|
|
}
|
|
}
|
|
/*TasksMenuItemWrapper {
|
|
id: testItem
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: menuItemHeight
|
|
|
|
text: "Test"
|
|
icon: "window-close"
|
|
onClicked: {
|
|
}
|
|
}*/
|
|
}
|
|
|
|
Rectangle {
|
|
id: bgRect
|
|
visible: secondaryColumn
|
|
anchors {
|
|
top: parent.top
|
|
bottom: bgStatic.top
|
|
left: parent.left
|
|
right: parent.right
|
|
leftMargin: 2
|
|
rightMargin: PlasmaCore.Units.smallSpacing-1
|
|
topMargin: PlasmaCore.Units.smallSpacing-1
|
|
}
|
|
gradient: Gradient {
|
|
GradientStop { position: 0; color: backgroundColorStatic }
|
|
GradientStop { position: 0.5; color: backgroundColorGradient }
|
|
GradientStop { position: 1; color: backgroundColorStatic }
|
|
}
|
|
z: -2
|
|
}
|
|
Rectangle {
|
|
id: bgStatic
|
|
anchors {
|
|
top: staticMenuItems.top
|
|
bottom: parent.bottom
|
|
left: parent.left
|
|
right: parent.right
|
|
leftMargin: 2
|
|
rightMargin: PlasmaCore.Units.smallSpacing
|
|
bottomMargin: 2
|
|
}
|
|
Rectangle {
|
|
id: bgStaticBorderLine
|
|
anchors {
|
|
top: parent.top
|
|
left: parent.left
|
|
right: parent.right
|
|
}
|
|
height: PlasmaCore.Units.smallSpacing
|
|
gradient: Gradient {
|
|
GradientStop { position: 0; color: borderColor }
|
|
GradientStop { position: 1; color: "transparent"}
|
|
}
|
|
}
|
|
z: -1
|
|
color: backgroundColorStatic
|
|
}
|
|
function decreaseItemIndex() {
|
|
currentItemIndex--;
|
|
if(currentItemIndex < 0) {
|
|
currentItemIndex = menuitems.children.length + staticMenuItems.children.length - 1;
|
|
}
|
|
var temp = currentItemIndex;
|
|
var container = menuitems.children;
|
|
if(currentItemIndex >= menuitems.children.length) {
|
|
temp -= menuitems.children.length;
|
|
container = staticMenuItems.children;
|
|
}
|
|
if(container[temp].objectName !== "menuitemwrapper" || (container[temp].objectName === "menuitemwrapper" && (!container[temp].enabled || !container[temp].visible))) {
|
|
decreaseItemIndex();
|
|
} else {
|
|
if(currentItem !== null) currentItem.selected = false;
|
|
container[temp].selected = true;
|
|
currentItem = container[temp];
|
|
}
|
|
|
|
}
|
|
function increaseItemIndex() {
|
|
currentItemIndex++;
|
|
if(currentItemIndex == menuitems.children.length + staticMenuItems.children.length) {
|
|
currentItemIndex = 0;
|
|
}
|
|
var temp = currentItemIndex;
|
|
var container = menuitems.children;
|
|
if(currentItemIndex >= menuitems.children.length) {
|
|
temp -= menuitems.children.length;
|
|
container = staticMenuItems.children;
|
|
}
|
|
if(container[temp].objectName !== "menuitemwrapper" || (container[temp].objectName === "menuitemwrapper" && (!container[temp].enabled || !container[temp].visible))) {
|
|
increaseItemIndex();
|
|
} else {
|
|
if(currentItem !== null) currentItem.selected = false;
|
|
container[temp].selected = true;
|
|
currentItem = container[temp];
|
|
}
|
|
|
|
}
|
|
Keys.onPressed: {
|
|
if(event.key == Qt.Key_Up) {
|
|
decreaseItemIndex();
|
|
}
|
|
else if(event.key == Qt.Key_Down || event.key == Qt.Key_Tab) {
|
|
increaseItemIndex();
|
|
}
|
|
else if(event.key == Qt.Key_Escape) {
|
|
tasksMenu.closeMenu();
|
|
}
|
|
else if(event.key == Qt.Key_Enter || event.key == Qt.Key_Return) {
|
|
if(currentItem !== null) {
|
|
currentItem.clicked();
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
* Connects the context menu with the C++ part of the plasmoid.
|
|
* The native interface installs itself onto this dialog as an event filter, upon which
|
|
* all mouse click events are captured. By checking if the mouse has been clicked outside of
|
|
* the context menu, we can then safely close it.
|
|
*
|
|
* This works because right after creating the context menu, we have set this dialog window to
|
|
* grab all mouse events, which mimicks the way context menus work under Linux.
|
|
*
|
|
*/
|
|
Connections {
|
|
target: plasmoid.nativeInterface;
|
|
function onMouseEventDetected(mouse) {
|
|
if(!fscope.contains(plasmoid.nativeInterface.getPosition(tasksMenu))) {
|
|
tasksMenu.closeMenu();
|
|
}
|
|
}
|
|
/*onMouseEventDetected: {
|
|
|
|
}*/
|
|
}
|
|
|
|
}
|
|
|
|
Component.onCompleted: {
|
|
backend.showAllPlaces.connect(showContextMenuWithAllPlaces)
|
|
tasksMenu.backgroundHints = 2; // Sets the dialog background to the solid SVG variant.
|
|
}
|
|
Component.onDestruction: {
|
|
backend.showAllPlaces.disconnect(showContextMenuWithAllPlaces)
|
|
if(alsoCloseTask)
|
|
tasksModel.requestClose(modelIndex);
|
|
}
|
|
}
|