/*SPDX-FileCopyrightText: 2016 Eike Hein <hein@kde.org>SPDX-FileCopyrightText: 2022 Carson Black <uhhadd@gmail.com>SPDX-License-Identifier: GPL-2.0-or-later*/pragma ComponentBehavior: Boundimport QtQuickimport QtQuick.Layoutsimport org.kde.plasma.plasmoidimport org.kde.plasma.core as PlasmaCoreimport org.kde.plasma.components as PlasmaComponentsimport org.kde.plasma.extras as PlasmaExtrasimport org.kde.kirigami as Kirigamiimport org.kde.taskmanager as TaskManagerPlasmoidItem {id: rootproperty string lastActiveTaskName: ""property /*QIcon*/ var lastActiveTaskIcon: ""Plasmoid.constraintHints: Plasmoid.CanFillAreacompactRepresentation: windowListButtonfullRepresentation: windowListswitchWidth: Kirigami.Units.gridUnit * 8switchHeight: Kirigami.Units.gridUnit * 6readonly property bool inPanel: [PlasmaCore.Types.TopEdge,PlasmaCore.Types.RightEdge,PlasmaCore.Types.BottomEdge,PlasmaCore.Types.LeftEdge,].includes(Plasmoid.location)TextMetrics {id: placeholderMetricsfont: Kirigami.Theme.defaultFonttext: i18nc("@info:placeholder", "No open windows")}property ListModel noWindowModel: ListModel {ListElement {display: ""decoration: "edit-none"}Component.onCompleted: {noWindowModel.setProperty(0, "display", placeholderMetrics.text)}}TaskManager.VirtualDesktopInfo {id: virtualDesktopInfo}TaskManager.ActivityInfo {id: activityInforeadonly property string nullUuid: "00000000-0000-0000-0000-000000000000"}TaskManager.TasksModel {id: tasksModelscreenGeometry: Plasmoid.containment.screenGeometryactivity: activityInfo.currentActivitysortMode: Plasmoid.configuration.sortingStrategygroupMode: TaskManager.TasksModel.GroupDisabledfilterByCurrentVirtualDesktop: Plasmoid.configuration.showOnlyCurrentDesktopfilterByScreen: Plasmoid.configuration.showOnlyCurrentScreenfilterByActivity: Plasmoid.configuration.showOnlyCurrentActivityfilterNotMinimized: Plasmoid.configuration.showOnlyMinimized}property string longestWindowCaption: ""TextMetrics {id: longestTextMetricselide: Text.ElideRight}property int fullRepresentationDynamicWidth: 0function updateLongestWindowTitle() {if (!tasksModel || !tasksModel.count) {longestWindowCaption = "";fullRepresentationDynamicWidth = Math.ceil(placeholderMetrics.width)+ Kirigami.Units.iconSizes.sizeForLabels * 2 + Kirigami.Units.smallSpacing * 2;return;}let maxWidth = 0;let longest = "";for (let i = 0; i < tasksModel.count; ++i) {let idx = tasksModel.makeModelIndex(i);longestTextMetrics.text = tasksModel.data(idx, 0) || tasksModel.data(idx, TaskManager.AbstractTasksModel.AppName) || "";if (longestTextMetrics.width > maxWidth) {maxWidth = longestTextMetrics.width;longest = longestTextMetrics.text;}}root.longestWindowCaption = longest;fullRepresentationDynamicWidth = Math.ceil(maxWidth) + Kirigami.Units.iconSizes.sizeForLabels * 2 + Kirigami.Units.smallSpacing * 2;}Connections {target: tasksModelfunction onModelReset() { root.updateLongestWindowTitle(); }}Component {id: windowListListView {id: windowListViewproperty int maxDelegateWidth: 0clip: true// Set preferred size when on desktop containment.// Size set arbitrarily to fit approximately 12-14 items.Binding {target: windowListViewproperty: "Layout.preferredWidth"when: !root.inPanelvalue: Kirigami.Units.gridUnit * 28}Binding {target: windowListViewproperty: "Layout.preferredHeight"when: !root.inPanelvalue: Kirigami.Units.gridUnit * 24}Binding {target: windowListViewproperty: "Layout.maximumHeight"when: root.inPanelvalue: windowListView.contentHeight}Binding {target: windowListViewproperty: "Layout.minimumHeight"when: root.inPanelvalue: windowListView.contentHeight}Binding {target: windowListViewproperty: "Layout.maximumWidth"when: root.inPanelvalue: root.fullRepresentationDynamicWidth}Binding {target: windowListViewproperty: "Layout.minimumWidth"when: root.inPanelvalue: root.fullRepresentationDynamicWidth}model: root.inPanel && tasksModel.count === 0 ? root.noWindowModel : tasksModelConnections {target: rootfunction onExpandedChanged() {if (root.expanded) {windowListView.currentIndex = -1// Needed for when for expanded with Global Shortcutif (tasksModel.activeTask.valid) {root.lastActiveTaskName = tasksModel.data(tasksModel.activeTask, TaskManager.AbstractTasksModel.AppName) ||tasksModel.data(tasksModel.activeTask, 0 /* display name, window title if app name not present */)root.lastActiveTaskIcon = tasksModel.data(tasksModel.activeTask, 1 /* decorationrole */)}root.updateLongestWindowTitle();} else {Qt.callLater( () => { // give the previous window a chance to gain focusroot.lastActiveTaskName = ""root.lastActiveTaskIcon = ""root.updateLongestWindowTitle();})}}}// focus is needed to receive key events on desktop containment works in panel without this.focus: truekeyNavigationWraps: truehighlight: PlasmaExtras.Highlight {visible: windowListView.currentItemactive: windowListView.focuspressed: windowListView.currentItem && windowListView.currentItem.pressed}highlightMoveDuration: 0highlightResizeDuration: 0Keys.onTabPressed: {incrementCurrentIndex();}Keys.onBacktabPressed: {decrementCurrentIndex();}section {property: switch (Plasmoid.configuration.sortingStrategy) {case TaskManager.TasksModel.SortVirtualDesktop:return "VirtualDesktops"; // AbstractTasksModel::AdditionalRoles::VirtualDesktopscase TaskManager.TasksModel.SortActivity:return "Activities"; // AbstractTasksModel::AdditionalRoles::Activitiesdefault:return "";}delegate: Kirigami.ListSectionHeader {required property var sectionwidth: windowListView.widthtext: {switch (Plasmoid.configuration.sortingStrategy) {case TaskManager.TasksModel.SortVirtualDesktop:// Section contains the virtual desktop id. In case the application is on multiple desktops, it is empty.return section ? virtualDesktopInfo.desktopNames[virtualDesktopInfo.desktopIds.indexOf(section)] : "";case TaskManager.TasksModel.SortActivity:// Section contains the activity id. In case the application is on multiple activities, it is empty.return section ? activityInfo.activityName(section) : "";}return "";}}}// Helps with performance otherwise scrolling is very laggy and stuttery// with low fpsreuseItems: truedelegate: PlasmaComponents.ItemDelegate {id: delegaterequired property var modelrequired property var decorationwidth: {if (root.inPanel) {return root.fullRepresentationDynamicWidth} else {return ListView.view.width;}}highlighted: ListView.isCurrentItemcontentItem: RowLayout {spacing: Kirigami.Units.smallSpacingKirigami.Icon {id: iconItemsource: delegate.decorationvisible: source !== "" && iconItem.validimplicitWidth: Kirigami.Units.iconSizes.sizeForLabelsimplicitHeight: Kirigami.Units.iconSizes.sizeForLabels}// Fall back to a generic icon if the application doesn't provide a valid oneKirigami.Icon {source: "preferences-system-windows"visible: !iconItem.validimplicitWidth: Kirigami.Units.iconSizes.sizeForLabelsimplicitHeight: Kirigami.Units.iconSizes.sizeForLabels}PlasmaComponents.Label {Layout.fillWidth: truetext: delegate.model.displaytextFormat: Text.PlainTextelide: Text.ElideRight}}onHoveredChanged: {if (hovered) {windowListView.currentIndex = model.index}}onClicked: {windowListView.currentIndex = model.indextasksModel.requestActivate(tasksModel.makeModelIndex(model.index))focus = false // or it persists invisibly after closing}Keys.onReturnPressed: animateClick()Keys.onEnterPressed: animateClick()TapHandler {acceptedButtons: Qt.RightButtononTapped: { /* Empty to disable right click menu provided by applet on the delegate */ }}}Kirigami.PlaceholderMessage {anchors.centerIn: parentwidth: parent.width - (Kirigami.Units.largeSpacing * 2)visible: !root.inPanel && windowListView.count === 0icon.source: "edit-none"text: placeholderMetrics.text}}}// Only exists because the default CompactRepresentation doesn't expose the// ability to show text below or beside the icon.// TODO remove once it gains that feature.Component {id: windowListButtonMenuButton {id: menuButtonLayout.minimumWidth: Math.min(implicitWidth, Kirigami.Units.gridUnit * 20)Layout.maximumWidth: Layout.minimumWidthLayout.fillHeight: Plasmoid.formFactor === PlasmaCore.Types.HorizontalLayout.fillWidth: Plasmoid.formFactor === PlasmaCore.Types.VerticalonClicked: root.expanded = !root.expandeddown: pressed || root.expandedAccessible.name: Plasmoid.titleAccessible.description: root.toolTipSubTexttext: if (root.lastActiveTaskName !== "") {return root.lastActiveTaskName} else if (tasksModel.activeTask.valid) {return tasksModel.data(tasksModel.activeTask, TaskManager.AbstractTasksModel.AppName) ||tasksModel.data(tasksModel.activeTask, 0 /* display name, window title if app name not present */)} else {return i18nc("@title:window title shown e.g. for desktop and expanded widgets", "Plasma Desktop")}iconSource: if (root.lastActiveTaskIcon) {return root.lastActiveTaskIcon} else if (tasksModel.activeTask.valid) {return tasksModel.data(tasksModel.activeTask, 1 /* decorationrole */)} else {return "start-here-kde-symbolic"}Timer {id: hoverOpenTimerinterval: Plasmoid?.configuration?.hoverOpenDelay ?? 300repeat: falseonTriggered: {root.expanded = true}}onHoveredChanged: {if (hovered) {if (Plasmoid.configuration.openOnHover) {hoverOpenTimer.start()}} else {hoverOpenTimer.stop()}}}}}