diff --git a/CMakeLists.txt b/CMakeLists.txt index 9a0767b53..7e8b261cf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,179 +1,178 @@ project(plasma-desktop) set(PROJECT_VERSION "5.10.2") - set(PROJECT_VERSION_MAJOR 5) cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR) set(QT_MIN_VERSION "5.7.0") find_package(ECM 0.0.11 REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings NO_POLICY_SCOPE) include(ECMInstallIcons) include(ECMMarkAsTest) include(ECMMarkNonGuiExecutable) include(ECMOptionalAddSubdirectory) include(ECMQtDeclareLoggingCategory) include(FeatureSummary) include(CheckIncludeFiles) find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS Quick QuickWidgets DBus Widgets X11Extras Svg Concurrent ) set(KF5_MIN_VERSION "5.34.0") find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Auth Plasma PlasmaQuick DocTools I18n KCMUtils NewStuff KDELibs4Support Notifications NotifyConfig Attica Wallet Runner GlobalAccel Declarative People DBusAddons Activities ActivitiesStats Config ) find_package(LibKWorkspace CONFIG REQUIRED) find_package(LibTaskManager CONFIG REQUIRED) find_package(KWinDBusInterface CONFIG REQUIRED) find_package(ScreenSaverDBusInterface CONFIG REQUIRED) find_package(KRunnerAppDBusInterface CONFIG REQUIRED) find_package(KSMServerDBusInterface CONFIG REQUIRED) find_package(KF5ItemModels CONFIG REQUIRED) find_package(KF5Emoticons CONFIG REQUIRED) find_package(AppStreamQt 0.10.4) set_package_properties(AppStreamQt PROPERTIES DESCRIPTION "Appstream integration" TYPE RECOMMENDED PURPOSE "Needed to allow appstream integration from application menus" ) find_package(KF5Baloo 5.15) set_package_properties(KF5Baloo PROPERTIES DESCRIPTION "File Searching" TYPE RECOMMENDED PURPOSE "Needed to build the File Search KCM" ) find_package(Fontconfig) set_package_properties(Fontconfig PROPERTIES DESCRIPTION "Font access configuration library" URL "http://www.freedesktop.org/wiki/Software/fontconfig" TYPE OPTIONAL PURPOSE "Needed to build font configuration and installation tools" ) find_package(X11) set_package_properties(X11 PROPERTIES DESCRIPTION "X11 libraries" URL "http://www.x.org" TYPE REQUIRED PURPOSE "Required for building the X11 based workspace" ) find_package(UDev) set_package_properties(UDev PROPERTIES DESCRIPTION "UDev library" URL "http://www.kernel.org/pub/linux/utils/kernel/hotplug/udev.html" TYPE OPTIONAL PURPOSE "Required for device discovery in keyboard daemon" ) find_package(XCB REQUIRED COMPONENTS XCB SHM IMAGE OPTIONAL_COMPONENTS XKB XINPUT ) set_package_properties(XCB PROPERTIES TYPE REQUIRED) add_feature_info("XCB-XKB" XCB_XKB_FOUND "Required for building kcm/keyboard") add_feature_info("libxft" X11_Xft_FOUND "X FreeType interface library required for font installation") find_package(Evdev) set_package_properties(Evdev PROPERTIES TYPE OPTIONAL) add_feature_info("Evdev" EVDEV_FOUND "Evdev driver headers needed for input KCM") find_package(Synaptics) set_package_properties(Synaptics PROPERTIES TYPE OPTIONAL) add_feature_info("Synaptics" SYNAPTICS_FOUND "Synaptics libraries needed for touchpad KCM") include(ConfigureChecks.cmake) if(${Qt5Gui_OPENGL_IMPLEMENTATION} STREQUAL "GL") find_package(OpenGL) set_package_properties(OpenGL PROPERTIES DESCRIPTION "The OpenGL libraries" URL "http://www.opengl.org" TYPE REQUIRED ) else() find_package(OpenGLES) set_package_properties(OpenGLES PROPERTIES DESCRIPTION "The OpenGLES libraries" URL "http://www.khronos.org/opengles" TYPE REQUIRED ) endif() find_package(Breeze ${PROJECT_VERSION} CONFIG) set_package_properties(Breeze PROPERTIES TYPE OPTIONAL PURPOSE "For setting the default window decoration plugin") if(${Breeze_FOUND}) if(${BREEZE_WITH_KDECORATION}) set(HAVE_BREEZE_DECO true) else() set(HAVE_BREEZE_DECO FALSE) endif() else() set(HAVE_BREEZE_DECO FALSE) endif() if(${AppStreamQt_FOUND}) set(HAVE_APPSTREAMQT true) endif() include_directories("${CMAKE_CURRENT_BINARY_DIR}") configure_file(config-workspace.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-workspace.h) configure_file(config-unix.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-unix.h ) configure_file(config-appstream.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-appstream.h ) configure_file(config-X11.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-X11.h) configure_file(config-runtime.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-runtime.h) plasma_install_package(desktoppackage org.kde.plasma.desktop shells shell) add_definitions(-DQT_NO_URL_CAST_FROM_STRING) add_subdirectory(layout-templates) add_subdirectory(doc) add_subdirectory(runners) add_subdirectory(containments) add_subdirectory(toolboxes) add_subdirectory(applets) add_subdirectory(dataengines) add_subdirectory(kcms) add_subdirectory(knetattach) add_subdirectory(attica-kde) add_subdirectory(imports/activitymanager/) add_subdirectory(solid-device-automounter) if(X11_Xkb_FOUND AND XCB_XKB_FOUND) add_subdirectory(kaccess) endif() install(FILES org.kde.plasmashell.metainfo.xml DESTINATION ${KDE_INSTALL_METAINFODIR}) feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/applets/taskmanager/package/contents/ui/GroupDialog.qml b/applets/taskmanager/package/contents/ui/GroupDialog.qml index 4b1fee505..08d2550f0 100644 --- a/applets/taskmanager/package/contents/ui/GroupDialog.qml +++ b/applets/taskmanager/package/contents/ui/GroupDialog.qml @@ -1,248 +1,283 @@ /*************************************************************************** * Copyright (C) 2012-2013 by Eike Hein * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ import QtQuick 2.4 import QtQuick.Window 2.2 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.extras 2.0 as PlasmaExtras import org.kde.draganddrop 2.0 import "../code/layout.js" as LayoutManager PlasmaCore.Dialog { id: groupDialog visible: false type: PlasmaCore.Dialog.PopupMenu flags: Qt.WindowStaysOnTopHint hideOnWindowDeactivate: true location: plasmoid.location - property int preferredWidth: Screen.width / (3 * Screen.devicePixelRatio) - property int preferredHeight: Screen.height / (2 * Screen.devicePixelRatio) - property int contentWidth: scrollArea.overflowing ? mainItem.width - (units.smallSpacing * 3) : mainItem.width - property TextMetrics textMetrics: TextMetrics {} + readonly property int preferredWidth: Screen.width / (3 * Screen.devicePixelRatio) + readonly property int preferredHeight: Screen.height / (2 * Screen.devicePixelRatio) + readonly property int contentWidth: scrollArea.overflowing ? mainItem.width - (units.smallSpacing * 3) : mainItem.width + readonly property TextMetrics textMetrics: TextMetrics {} property alias overflowing: scrollArea.overflowing property alias activeTask: focusActiveTaskTimer.targetIndex + property var _oldAppletStatus: PlasmaCore.Types.UnknownStatus function selectTask(task) { if (!task) { return; } task.forceActiveFocus(); scrollArea.ensureItemVisible(task); } - mainItem: PlasmaExtras.ScrollArea { - id: scrollArea + mainItem: MouseHandler { + id: mouseHandler - property bool overflowing: (viewport.height < contentItem.height) + target: taskList + handleWheelEvents: !scrollArea.overflowing - horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff + Timer { + id: focusActiveTaskTimer - function ensureItemVisible(item) { - var itemTop = item.y; - var itemBottom = (item.y + item.height); + property var targetIndex: null - if (itemTop < flickableItem.contentY) { - flickableItem.contentY = itemTop; - } + interval: 0 + repeat: false - if ((itemBottom - flickableItem.contentY) > viewport.height) { - flickableItem.contentY = Math.abs(viewport.height - itemBottom); - } - } + onTriggered: { + // Now we can home in on the previously active task + // collected in groupDialog.onVisibleChanged. - MouseHandler { - id: mouseHandler + if (targetIndex != null) { + for (var i = 0; i < groupRepeater.count; ++i) { + var task = groupRepeater.itemAt(i); - width: parent.width - height: (groupRepeater.count * (LayoutManager.verticalMargins() - + Math.max(theme.mSize(theme.defaultFont).height, units.iconSizes.medium))) + if (task.modelIndex() == targetIndex) { + selectTask(task); + return; + } + } + } + } + } - target: taskList - handleWheelEvents: !scrollArea.overflowing + PlasmaExtras.ScrollArea { + id: scrollArea - Timer { - id: focusActiveTaskTimer + anchors.fill: parent - property var targetIndex: null + readonly property bool overflowing: (viewport.height < contentItem.height) - interval: 0 - repeat: false + horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff - onTriggered: { - // Now we can home in on the previously active task - // collected in groupDialog.onVisibleChanged. + function ensureItemVisible(item) { + var itemTop = item.y; + var itemBottom = (item.y + item.height); - if (targetIndex != null) { - for (var i = 0; i < groupRepeater.count; ++i) { - var task = groupRepeater.itemAt(i); + if (itemTop < flickableItem.contentY) { + flickableItem.contentY = itemTop; + } - if (task.modelIndex() == targetIndex) { - selectTask(task); - return; - } - } - } + if ((itemBottom - flickableItem.contentY) > viewport.height) { + flickableItem.contentY = Math.abs(viewport.height - itemBottom); } } TaskList { id: taskList - anchors.fill: parent + width: parent.width add: Transition { // We trigger a null-interval timer in the first add // transition after setting the model so onTriggered // will run after the Flow has positioned items. ScriptAction { script: { if (groupRepeater.aboutToPopulate) { focusActiveTaskTimer.restart(); groupRepeater.aboutToPopulate = false; } } } } Repeater { id: groupRepeater property bool aboutToPopulate: false function currentIndex() { for (var i = 0; i < count; ++i) { if (itemAt(i).activeFocus) { return i; } } return -1; } onCountChanged: updateSize(); } } } Keys.onUpPressed: { var currentIndex = groupRepeater.currentIndex(); // In doubt focus the last item, so we start at the bottom when user // initially presses up. if (currentIndex === -1) { selectTask(groupRepeater.itemAt(groupRepeater.count - 1)); return; } var previousIndex = currentIndex - 1; if (previousIndex < 0) { previousIndex = groupRepeater.count - 1; } selectTask(groupRepeater.itemAt(previousIndex)); } Keys.onDownPressed: { var currentIndex = groupRepeater.currentIndex(); // In doubt focus the first item, also wrap around. if (currentIndex === -1 || currentIndex + 1 >= groupRepeater.count) { selectTask(groupRepeater.itemAt(0)); return; } selectTask(groupRepeater.itemAt(currentIndex + 1)); } Keys.onEscapePressed: groupDialog.visible = false; } data: [ VisualDataModel { id: groupFilter delegate: Task { visible: true inPopup: true } } ] onVisualParentChanged: { - if (!visualParent) { + if (visible && visualParent) { + attachModel(); + } else { visible = false; } } onVisibleChanged: { if (visible && visualParent) { - groupFilter.model = tasksModel; - groupFilter.rootIndex = groupFilter.modelIndex(visualParent.itemIndex); + _oldAppletStatus = plasmoid.status; + plasmoid.status = PlasmaCore.Types.RequiresAttentionStatus; - groupRepeater.aboutToPopulate = true; - groupRepeater.model = groupFilter; + attachModel(); - mainItem.forceActiveFocus(); + groupDialog.requestActivate(); + mouseHandler.forceActiveFocus(); } else { + plasmoid.status = _oldAppletStatus; visualParent = null; groupRepeater.model = undefined; groupFilter.model = undefined; groupFilter.rootIndex = undefined; } } + function attachModel() { + if (!visualParent) { + return; + } + + if (!groupFilter.model) { + groupFilter.model = tasksModel; + } + + groupRepeater.aboutToPopulate = true; + + groupFilter.rootIndex = tasksModel.makeModelIndex(visualParent.itemIndex); + + if (!groupRepeater.model) { + groupRepeater.model = groupFilter; + } + } + function updateSize() { - if (!visible || !visualParent) { + if (!visible) { + return; + } + + if (!visualParent) { + visible = false; return; } - if (!groupRepeater.count) { + if (!visualParent.childCount) { visible = false; - } else { + // Setting VisualDataModel.rootIndex drops groupRepeater.count to 0 + // before the actual row count. updateSize is therefore invoked twice; + // only update size once the repeater count matches the model role. + } else if (visualParent.childCount == groupRepeater.count) { var task; var maxWidth = 0; var maxHeight = 0; backend.cancelHighlightWindows(); for (var i = 0; i < taskList.children.length - 1; ++i) { task = taskList.children[i]; textMetrics.text = task.labelText; var textWidth = textMetrics.boundingRect.width; if (textWidth > maxWidth) { maxWidth = textWidth; } task.labelTextChanged.connect(updateSize); } - maxWidth += LayoutManager.horizontalMargins() + units.iconSizes.medium + 2 * units.smallSpacing; maxHeight = groupRepeater.count * (LayoutManager.verticalMargins() + Math.max(theme.mSize(theme.defaultFont).height, units.iconSizes.medium)); + maxWidth += LayoutManager.horizontalMargins() + units.iconSizes.medium + 2 * units.smallSpacing; + + // Add horizontal space for scrollbar if needed. + // FIXME TODO HACK: Use actuall scrollbar width instead of a good guess. + if (maxHeight > preferredHeight) { + maxWidth += (units.smallSpacing * 3); + } + mainItem.height = Math.min(preferredHeight, maxHeight); mainItem.width = Math.min(preferredWidth, (tasks.vertical ? Math.max(maxWidth, tasks.width) : Math.min(maxWidth, tasks.width))); } } } diff --git a/applets/taskmanager/package/contents/ui/main.qml b/applets/taskmanager/package/contents/ui/main.qml index bc22e9309..1f2a1c363 100644 --- a/applets/taskmanager/package/contents/ui/main.qml +++ b/applets/taskmanager/package/contents/ui/main.qml @@ -1,468 +1,469 @@ /*************************************************************************** * Copyright (C) 2012-2016 by Eike Hein * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * ***************************************************************************/ import QtQuick 2.0 import QtQuick.Layouts 1.1 import org.kde.plasma.plasmoid 2.0 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.taskmanager 0.1 as TaskManager import org.kde.plasma.private.taskmanager 0.1 as TaskManagerApplet import "../code/layout.js" as LayoutManager import "../code/tools.js" as TaskTools Item { id: tasks anchors.fill: parent property bool vertical: (plasmoid.formFactor == PlasmaCore.Types.Vertical) property bool iconsOnly: (plasmoid.pluginName == "org.kde.plasma.icontasks") property QtObject contextMenuComponent: Qt.createComponent("ContextMenu.qml"); Plasmoid.preferredRepresentation: Plasmoid.fullRepresentation Plasmoid.onUserConfiguringChanged: { if (plasmoid.userConfiguring) { groupDialog.visible = false; } } Layout.fillWidth: true Layout.fillHeight:true Layout.minimumWidth: tasks.vertical ? 0 : LayoutManager.preferredMinWidth() Layout.minimumHeight: !tasks.vertical ? 0 : LayoutManager.preferredMinHeight() //BEGIN TODO: this is not precise enough: launchers are smaller than full tasks Layout.preferredWidth: tasks.vertical ? units.gridUnit * 10 : ((LayoutManager.logicalTaskCount() * LayoutManager.preferredMaxWidth()) / LayoutManager.calculateStripes()); Layout.preferredHeight: tasks.vertical ? ((LayoutManager.logicalTaskCount() * LayoutManager.preferredMaxHeight()) / LayoutManager.calculateStripes()) : units.gridUnit * 2; //END TODO property Item dragSource: null signal requestLayout signal windowsHovered(variant winIds, bool hovered) signal presentWindows(variant winIds) onWidthChanged: { taskList.width = LayoutManager.layoutWidth(); if (plasmoid.configuration.forceStripes) { taskList.height = LayoutManager.layoutHeight(); } } onHeightChanged: { if (plasmoid.configuration.forceStripes) { taskList.width = LayoutManager.layoutWidth(); } taskList.height = LayoutManager.layoutHeight(); } onDragSourceChanged: { if (dragSource == null) { tasksModel.syncLaunchers(); } } TaskManager.TasksModel { id: tasksModel readonly property int logicalLauncherCount: { if (plasmoid.configuration.separateLaunchers) { return launcherCount; } var startupsWithLaunchers = 0; for (var i = 0; i < taskRepeater.count; ++i) { var item = taskRepeater.itemAt(i); if (item && item.m.IsStartup === true && item.m.HasLauncher === true) { ++startupsWithLaunchers; } } return launcherCount + startupsWithLaunchers; } virtualDesktop: virtualDesktopInfo.currentDesktop screenGeometry: plasmoid.screenGeometry activity: activityInfo.currentActivity filterByVirtualDesktop: plasmoid.configuration.showOnlyCurrentDesktop filterByScreen: plasmoid.configuration.showOnlyCurrentScreen filterByActivity: plasmoid.configuration.showOnlyCurrentActivity filterNotMinimized: plasmoid.configuration.showOnlyMinimized sortMode: iconsOnly ? TaskManager.TasksModel.SortManual : sortModeEnumValue(plasmoid.configuration.sortingStrategy) launchInPlace: iconsOnly separateLaunchers: { if (!iconsOnly && !plasmoid.configuration.separateLaunchers && plasmoid.configuration.sortingStrategy == 1) { return false; } return true; } groupMode: iconsOnly ? TaskManager.TasksModel.GroupApplications : groupModeEnumValue(plasmoid.configuration.groupingStrategy) groupInline: !plasmoid.configuration.groupPopups groupingWindowTasksThreshold: (plasmoid.configuration.onlyGroupWhenFull && !iconsOnly ? LayoutManager.optimumCapacity(width, height) + 1 : -1) onLauncherListChanged: { layoutTimer.restart(); plasmoid.configuration.launchers = launcherList; } onGroupingAppIdBlacklistChanged: { plasmoid.configuration.groupingAppIdBlacklist = groupingAppIdBlacklist; } onGroupingLauncherUrlBlacklistChanged: { plasmoid.configuration.groupingLauncherUrlBlacklist = groupingLauncherUrlBlacklist; } function sortModeEnumValue(index) { switch (index) { case 0: return TaskManager.TasksModel.SortDisabled; case 1: return TaskManager.TasksModel.SortManual; case 2: return TaskManager.TasksModel.SortAlpha; case 3: return TaskManager.TasksModel.SortVirtualDesktop; case 4: return TaskManager.TasksModel.SortActivity; default: return TaskManager.TasksModel.SortDisabled; } } function groupModeEnumValue(index) { switch (index) { case 0: return TaskManager.TasksModel.GroupDisabled; case 1: return TaskManager.TasksModel.GroupApplications; } } Component.onCompleted: { launcherList = plasmoid.configuration.launchers; groupingAppIdBlacklist = plasmoid.configuration.groupingAppIdBlacklist; groupingLauncherUrlBlacklist = plasmoid.configuration.groupingLauncherUrlBlacklist; // Only hook up view only after the above churn is done. taskRepeater.model = tasksModel; } } Connections { - enabled: plasmoid.configuration.groupPopups - target: tasksModel onActiveTaskChanged: { + if (!plasmoid.configuration.groupPopups) { + return; + } if (tasksModel.activeTask.parent.valid) { groupDialog.activeTask = tasksModel.activeTask; } } } TaskManager.VirtualDesktopInfo { id: virtualDesktopInfo } TaskManager.ActivityInfo { id: activityInfo } TaskManagerApplet.Backend { id: backend taskManagerItem: tasks toolTipItem: toolTipDelegate groupDialog: groupDialog highlightWindows: plasmoid.configuration.highlightWindows onAddLauncher: { tasks.addLauncher(url); } } PlasmaCore.DataSource { id: mpris2Source engine: "mpris2" connectedSources: sources function sourceNameForLauncherUrl(launcherUrl, pid) { if (!launcherUrl || launcherUrl == "") { return ""; } // MPRIS spec explicitly mentions that "DesktopEntry" is with .desktop extension trimmed // 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 = 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 && (pid === undefined || sourceData.InstancePid === pid)) { return source; } } return "" } function startOperation(source, op) { var service = serviceForSource(source) var operation = service.operationDescription(op) return service.startOperationCall(operation) } function goPrevious(source) { startOperation(source, "Previous"); } function goNext(source) { startOperation(source, "Next"); } function playPause(source) { startOperation(source, "PlayPause"); } function stop(source) { startOperation(source, "Stop"); } function raise(source) { startOperation(source, "Raise"); } function quit(source) { startOperation(source, "Quit"); } } Loader { id: pulseAudio source: "PulseAudio.qml" active: plasmoid.configuration.indicateAudioStreams } Timer { id: iconGeometryTimer interval: 500 repeat: false onTriggered: { TaskTools.publishIconGeometries(taskList.children); } } Binding { target: plasmoid property: "status" value: (tasksModel.anyTaskDemandsAttention ? PlasmaCore.Types.NeedsAttentionStatus : PlasmaCore.Types.PassiveStatus) } Connections { target: plasmoid onLocationChanged: { // This is on a timer because the panel may not have // settled into position yet when the location prop- // erty updates. iconGeometryTimer.start(); } } Connections { target: plasmoid.configuration onLaunchersChanged: tasksModel.launcherList = plasmoid.configuration.launchers onGroupingAppIdBlacklistChanged: tasksModel.groupingAppIdBlacklist = plasmoid.configuration.groupingAppIdBlacklist; onGroupingLauncherUrlBlacklistChanged: tasksModel.groupingLauncherUrlBlacklist = plasmoid.configuration.groupingLauncherUrlBlacklist; } TaskManagerApplet.DragHelper { id: dragHelper dragIconSize: units.iconSizes.medium } PlasmaCore.FrameSvgItem { id: taskFrame visible: false; imagePath: "widgets/tasks"; prefix: "normal" } PlasmaCore.Svg { id: taskSvg imagePath: "widgets/tasks" } MouseHandler { id: mouseHandler anchors.fill: parent target: taskList onUrlsDropped: { // If all dropped URLs point to application desktop files, we'll add a launcher for each of them. var createLaunchers = urls.every(function (item) { return backend.isApplication(item) }); if (createLaunchers) { urls.forEach(function (item) { addLauncher(item); }); return; } if (!hoveredItem) { return; } // DeclarativeMimeData urls is a QJsonArray but requestOpenUrls expects a proper QList. var urlsList = backend.jsonArrayToUrlList(urls); // Otherwise we'll just start a new instance of the application with the URLs as argument, // as you probably don't expect some of your files to open in the app and others to spawn launchers. tasksModel.requestOpenUrls(hoveredItem.modelIndex(), urlsList); } } ToolTipDelegate { id: toolTipDelegate visible: false } TaskList { id: taskList anchors { left: parent.left top: parent.top } onWidthChanged: LayoutManager.layout(taskRepeater) onHeightChanged: LayoutManager.layout(taskRepeater) flow: { if (tasks.vertical) { return plasmoid.configuration.forceStripes ? Flow.LeftToRight : Flow.TopToBottom } return plasmoid.configuration.forceStripes ? Flow.TopToBottom : Flow.LeftToRight } onAnimatingChanged: { if (!animating) { TaskTools.publishIconGeometries(children); } } function layout() { taskList.width = LayoutManager.layoutWidth(); taskList.height = LayoutManager.layoutHeight(); LayoutManager.layout(taskRepeater); } Timer { id: layoutTimer interval: 0 repeat: false onTriggered: taskList.layout() } Repeater { id: taskRepeater delegate: Task {} onItemAdded: taskList.layout() onItemRemoved: taskList.layout() } } GroupDialog { id: groupDialog } function hasLauncher(url) { return tasksModel.launcherPosition(url) != -1; } function addLauncher(url) { if (plasmoid.immutability !== PlasmaCore.Types.SystemImmutable) { tasksModel.requestAddLauncher(url); } } // This is called by plasmashell in response to a Meta+number shortcut. function activateTaskAtIndex(index) { if (typeof index !== "number") { return; } var task = taskRepeater.itemAt(index); if (task) { TaskTools.activateTask(task.modelIndex(), task.m, null, task); } } function resetDragSource() { dragSource = null; } function createContextMenu(rootTask, modelIndex, args) { var initialArgs = args || {} initialArgs.visualParent = rootTask; initialArgs.modelIndex = modelIndex; initialArgs.mpris2Source = mpris2Source; initialArgs.backend = backend; return tasks.contextMenuComponent.createObject(rootTask, initialArgs); } Component.onCompleted: { tasks.requestLayout.connect(layoutTimer.restart); tasks.requestLayout.connect(iconGeometryTimer.restart); tasks.windowsHovered.connect(backend.windowsHovered); tasks.presentWindows.connect(backend.presentWindows); dragHelper.dropped.connect(resetDragSource); } }