diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0e303ff1ef..97af6889ec 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -137,7 +137,6 @@ target_sources(kwin PRIVATE scene/workspacescene_opengl.cpp scene/workspacescene_qpainter.cpp screenedge.cpp - scripting/clientmodel.cpp scripting/dbuscall.cpp scripting/desktopbackgrounditem.cpp scripting/screenedgeitem.cpp @@ -147,6 +146,7 @@ target_sources(kwin PRIVATE scripting/scriptingutils.cpp scripting/tilemodel.cpp scripting/virtualdesktopmodel.cpp + scripting/windowmodel.cpp scripting/windowthumbnailitem.cpp scripting/workspace_wrapper.cpp shadow.cpp diff --git a/src/effects/desktopgrid/qml/DesktopView.qml b/src/effects/desktopgrid/qml/DesktopView.qml index f27a5db11c..4fb48ff1a5 100644 --- a/src/effects/desktopgrid/qml/DesktopView.qml +++ b/src/effects/desktopgrid/qml/DesktopView.qml @@ -16,7 +16,7 @@ import org.kde.kwin.private.desktopgrid 1.0 FocusScope { id: desktopView - required property QtObject clientModel + required property QtObject windowModel required property QtObject desktop required property var dndManagerStore readonly property bool dragActive: heap.dragActive || dragHandler.active || xAnim.running || yAnim.running @@ -65,22 +65,22 @@ FocusScope { } } Repeater { - model: KWinComponents.ClientFilterModel { + model: KWinComponents.WindowFilterModel { activity: KWinComponents.Workspace.currentActivity desktop: desktopView.desktop screenName: targetScreen.name - clientModel: desktopView.clientModel - windowType: KWinComponents.ClientFilterModel.Dock | KWinComponents.ClientFilterModel.Desktop + windowModel: desktopView.windowModel + windowType: KWinComponents.WindowFilterModel.Dock | KWinComponents.WindowFilterModel.Desktop } KWinComponents.WindowThumbnailItem { - wId: model.client.internalId - x: model.client.x - targetScreen.geometry.x - y: model.client.y - targetScreen.geometry.y - z: model.client.stackingOrder - width: model.client.width - height: model.client.height - opacity: model.client.dock ? desktopView.panelOpacity : 1 + wId: model.window.internalId + x: model.window.x - targetScreen.geometry.x + y: model.window.y - targetScreen.geometry.y + z: model.window.stackingOrder + width: model.window.width + height: model.window.height + opacity: model.window.dock ? desktopView.panelOpacity : 1 } } @@ -117,15 +117,15 @@ FocusScope { organized: container.organized layout.mode: effect.layout dndManagerStore: desktopView.dndManagerStore - model: KWinComponents.ClientFilterModel { + model: KWinComponents.WindowFilterModel { activity: KWinComponents.Workspace.currentActivity desktop: desktopView.desktop screenName: targetScreen.name - clientModel: desktopView.clientModel - windowType: ~KWinComponents.ClientFilterModel.Dock & - ~KWinComponents.ClientFilterModel.Desktop & - ~KWinComponents.ClientFilterModel.Notification & - ~KWinComponents.ClientFilterModel.CriticalNotification + windowModel: desktopView.windowModel + windowType: ~KWinComponents.WindowFilterModel.Dock & + ~KWinComponents.WindowFilterModel.Desktop & + ~KWinComponents.WindowFilterModel.Notification & + ~KWinComponents.WindowFilterModel.CriticalNotification } delegate: WindowHeapDelegate { windowHeap: heap diff --git a/src/effects/desktopgrid/qml/main.qml b/src/effects/desktopgrid/qml/main.qml index c3972530cb..87f322ed10 100644 --- a/src/effects/desktopgrid/qml/main.qml +++ b/src/effects/desktopgrid/qml/main.qml @@ -153,7 +153,7 @@ Rectangle { KWinComponents.VirtualDesktopModel { id: desktopModel } - KWinComponents.ClientModel { + KWinComponents.WindowModel { id: stackModel } @@ -268,7 +268,7 @@ Rectangle { width: container.width height: container.height - clientModel: stackModel + windowModel: stackModel dndManagerStore: container.dndManagerStore Rectangle { anchors.fill: parent diff --git a/src/effects/overview/qml/DesktopBar.qml b/src/effects/overview/qml/DesktopBar.qml index ff357ec3a9..5a86d32921 100644 --- a/src/effects/overview/qml/DesktopBar.qml +++ b/src/effects/overview/qml/DesktopBar.qml @@ -22,7 +22,7 @@ Item { readonly property real desktopWidth: desktopHeight * targetScreen.geometry.width / targetScreen.geometry.height readonly property real columnHeight: desktopHeight + PlasmaCore.Units.gridUnit - property QtObject clientModel + property QtObject windowModel property alias desktopModel: desktopRepeater.model property QtObject selectedDesktop: null property WindowHeap heap @@ -100,7 +100,7 @@ Item { width: targetScreen.geometry.width height: targetScreen.geometry.height visible: scaled - clientModel: bar.clientModel + windowModel: bar.windowModel desktop: delegate.desktop scale: bar.desktopHeight / targetScreen.geometry.height transformOrigin: Item.TopLeft diff --git a/src/effects/overview/qml/DesktopView.qml b/src/effects/overview/qml/DesktopView.qml index 0c38e14a44..21984d59e7 100644 --- a/src/effects/overview/qml/DesktopView.qml +++ b/src/effects/overview/qml/DesktopView.qml @@ -10,25 +10,25 @@ import org.kde.kwin 3.0 as KWinComponents Item { id: desktopView - required property QtObject clientModel + required property QtObject windowModel required property QtObject desktop Repeater { - model: KWinComponents.ClientFilterModel { + model: KWinComponents.WindowFilterModel { activity: KWinComponents.Workspace.currentActivity desktop: desktopView.desktop screenName: targetScreen.name - clientModel: desktopView.clientModel + windowModel: desktopView.windowModel } KWinComponents.WindowThumbnailItem { - wId: model.client.internalId - x: model.client.x - targetScreen.geometry.x - y: model.client.y - targetScreen.geometry.y - width: model.client.width - height: model.client.height - z: model.client.stackingOrder - visible: !model.client.minimized + wId: model.window.internalId + x: model.window.x - targetScreen.geometry.x + y: model.window.y - targetScreen.geometry.y + width: model.window.width + height: model.window.height + z: model.window.stackingOrder + visible: !model.window.minimized } } } diff --git a/src/effects/overview/qml/ScreenView.qml b/src/effects/overview/qml/ScreenView.qml index 0b78453c5f..4463f64b35 100644 --- a/src/effects/overview/qml/ScreenView.qml +++ b/src/effects/overview/qml/ScreenView.qml @@ -183,7 +183,7 @@ FocusScope { DesktopBar { id: bar anchors.fill: parent - clientModel: stackModel + windowModel: stackModel desktopModel: desktopModel selectedDesktop: KWinComponents.Workspace.currentDesktop heap: heap @@ -238,17 +238,17 @@ FocusScope { organized: container.organized Keys.priority: Keys.AfterItem Keys.forwardTo: searchResults - model: KWinComponents.ClientFilterModel { + model: KWinComponents.WindowFilterModel { activity: KWinComponents.Workspace.currentActivity desktop: KWinComponents.Workspace.currentDesktop screenName: targetScreen.name - clientModel: stackModel + windowModel: stackModel filter: effect.searchText minimizedWindows: !effect.ignoreMinimized - windowType: ~KWinComponents.ClientFilterModel.Dock & - ~KWinComponents.ClientFilterModel.Desktop & - ~KWinComponents.ClientFilterModel.Notification & - ~KWinComponents.ClientFilterModel.CriticalNotification + windowType: ~KWinComponents.WindowFilterModel.Dock & + ~KWinComponents.WindowFilterModel.Desktop & + ~KWinComponents.WindowFilterModel.Notification & + ~KWinComponents.WindowFilterModel.CriticalNotification } delegate: WindowHeapDelegate { windowHeap: heap @@ -267,7 +267,7 @@ FocusScope { } opacity: 1 - downGestureProgress - onDownGestureTriggered: client.closeWindow() + onDownGestureTriggered: window.closeWindow() } onActivated: effect.deactivate(); onWindowClicked: { @@ -294,25 +294,25 @@ FocusScope { } Repeater { - model: KWinComponents.ClientFilterModel { + model: KWinComponents.WindowFilterModel { desktop: KWinComponents.Workspace.currentDesktop screenName: targetScreen.name - clientModel: stackModel - windowType: KWinComponents.ClientFilterModel.Dock + windowModel: stackModel + windowType: KWinComponents.WindowFilterModel.Dock } KWinComponents.WindowThumbnailItem { id: windowThumbnail - visible: !model.client.hidden && opacity > 0 - wId: model.client.internalId - x: model.client.x - targetScreen.geometry.x - y: model.client.y - targetScreen.geometry.y - z: model.client.stackingOrder - width: model.client.width - height: model.client.height + visible: !model.window.hidden && opacity > 0 + wId: model.window.internalId + x: model.window.x - targetScreen.geometry.x + y: model.window.y - targetScreen.geometry.y + z: model.window.stackingOrder + width: model.window.width + height: model.window.height opacity: container.effect.gestureInProgress ? 1 - container.effect.partialActivationFactor - : (model.client.hidden || container.organized) ? 0 : 1 + : (model.window.hidden || container.organized) ? 0 : 1 Behavior on opacity { enabled: !container.effect.gestureInProgress @@ -321,7 +321,7 @@ FocusScope { } } - KWinComponents.ClientModel { + KWinComponents.WindowModel { id: stackModel } diff --git a/src/effects/private/qml/WindowHeap.qml b/src/effects/private/qml/WindowHeap.qml index 9267eedae5..e29d49b8e2 100644 --- a/src/effects/private/qml/WindowHeap.qml +++ b/src/effects/private/qml/WindowHeap.qml @@ -43,7 +43,7 @@ FocusScope { property bool animationEnabled: false property bool absolutePositioning: true property real padding: 0 - // Either a string "activeClass" or a list internalIds of clients + // Either a string "activeClass" or a list internalIds of windows property var showOnly: [] required property bool organized @@ -55,7 +55,7 @@ FocusScope { signal windowClicked(QtObject window, EventPoint eventPoint) function activateIndex(index) { - KWinComponents.Workspace.activeClient = windowsInstantiator.objectAt(index).client; + KWinComponents.Workspace.activeClient = windowsInstantiator.objectAt(index).window; activated(); } @@ -142,7 +142,7 @@ FocusScope { onObjectAdded: (index, object) => { object.parent = expoLayout - var key = object.client.internalId; + var key = object.window.internalId; if (heap.containsDND(key)) { expoLayout.forceLayout(); var oldGlobalRect = heap.restoreDND(key); @@ -372,7 +372,7 @@ FocusScope { } if (selectedItem) { handled = true; - KWinComponents.Workspace.activeClient = selectedItem.client; + KWinComponents.Workspace.activeClient = selectedItem.window; activated(); } break; diff --git a/src/effects/private/qml/WindowHeapDelegate.qml b/src/effects/private/qml/WindowHeapDelegate.qml index 0063eed973..e0c0b9f99a 100644 --- a/src/effects/private/qml/WindowHeapDelegate.qml +++ b/src/effects/private/qml/WindowHeapDelegate.qml @@ -16,25 +16,25 @@ import org.kde.plasma.core 2.0 as PlasmaCore Item { id: thumb - required property QtObject client + required property QtObject window required property int index required property Item windowHeap readonly property bool selected: windowHeap.selectedIndex === index // no desktops is a special value which means "All Desktops" - readonly property bool presentOnCurrentDesktop: !client.desktops.length || client.desktops.indexOf(KWinComponents.Workspace.currentDesktop) !== -1 - readonly property bool initialHidden: client.minimized || !presentOnCurrentDesktop + readonly property bool presentOnCurrentDesktop: !window.desktops.length || window.desktops.indexOf(KWinComponents.Workspace.currentDesktop) !== -1 + readonly property bool initialHidden: window.minimized || !presentOnCurrentDesktop readonly property bool activeHidden: { if (windowHeap.showOnly === "activeClass") { if (!KWinComponents.Workspace.activeClient) { return true; } else { - return KWinComponents.Workspace.activeClient.resourceName !== client.resourceName; + return KWinComponents.Workspace.activeClient.resourceName !== window.resourceName; } } else { return windowHeap.showOnly.length !== 0 - && windowHeap.showOnly.indexOf(client.internalId) === -1; + && windowHeap.showOnly.indexOf(window.internalId) === -1; } } @@ -70,7 +70,7 @@ Item { visible: opacity > 0 z: (activeDragHandler.active || returning.running) ? 1000 - : client.stackingOrder * (presentOnCurrentDesktop ? 1 : 0.001) + : window.stackingOrder * (presentOnCurrentDesktop ? 1 : 0.001) function restoreDND(oldGlobalRect: rect) { thumbSource.restoreDND(oldGlobalRect); @@ -91,11 +91,11 @@ Item { KWinComponents.WindowThumbnailItem { id: thumbSource - wId: thumb.client.internalId + wId: thumb.window.internalId Drag.proposedAction: Qt.MoveAction Drag.supportedActions: Qt.MoveAction - Drag.source: thumb.client + Drag.source: thumb.window Drag.hotSpot: Qt.point( thumb.activeDragHandler.centroid.pressPosition.x * thumb.targetScale, thumb.activeDragHandler.centroid.pressPosition.y * thumb.targetScale) @@ -105,7 +105,7 @@ Item { function saveDND() { const oldGlobalRect = mapToItem(null, 0, 0, width, height); - thumb.windowHeap.saveDND(thumb.client.internalId, oldGlobalRect); + thumb.windowHeap.saveDND(thumb.window.internalId, oldGlobalRect); } function restoreDND(oldGlobalRect: rect) { thumb.substate = "reparenting"; @@ -120,7 +120,7 @@ Item { thumb.substate = "normal"; } function deleteDND() { - thumb.windowHeap.deleteDND(thumb.client.internalId); + thumb.windowHeap.deleteDND(thumb.window.internalId); } PlasmaCore.FrameSvgItem { @@ -157,7 +157,7 @@ Item { id: icon width: PlasmaCore.Units.iconSizes.large height: PlasmaCore.Units.iconSizes.large - source: thumb.client.icon + source: thumb.window.icon usesPlasmaTheme: false anchors.horizontalCenter: thumbSource.horizontalCenter anchors.bottom: thumbSource.bottom @@ -171,7 +171,7 @@ Item { anchors.top: parent.bottom anchors.horizontalCenter: parent.horizontalCenter elide: Text.ElideRight - text: thumb.client.caption + text: thumb.window.caption horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter } @@ -181,11 +181,11 @@ Item { id: cell layout: windowHeap.layout enabled: !thumb.activeHidden - naturalX: thumb.client.x - naturalY: thumb.client.y - naturalWidth: thumb.client.width - naturalHeight: thumb.client.height - persistentKey: thumb.client.internalId + naturalX: thumb.window.x + naturalY: thumb.window.y + naturalWidth: thumb.window.width + naturalHeight: thumb.window.height + persistentKey: thumb.window.internalId bottomMargin: icon.height / 4 + (thumb.windowTitleVisible ? caption.height : 0) } @@ -194,17 +194,17 @@ Item { name: "initial" PropertyChanges { target: thumb - x: thumb.client.x - targetScreen.geometry.x - (thumb.windowHeap.absolutePositioning ? windowHeap.layout.Kirigami.ScenePosition.x : 0) - y: thumb.client.y - targetScreen.geometry.y - (thumb.windowHeap.absolutePositioning ? windowHeap.layout.Kirigami.ScenePosition.y : 0) - width: thumb.client.width - height: thumb.client.height + x: thumb.window.x - targetScreen.geometry.x - (thumb.windowHeap.absolutePositioning ? windowHeap.layout.Kirigami.ScenePosition.x : 0) + y: thumb.window.y - targetScreen.geometry.y - (thumb.windowHeap.absolutePositioning ? windowHeap.layout.Kirigami.ScenePosition.y : 0) + width: thumb.window.width + height: thumb.window.height } PropertyChanges { target: thumbSource x: 0 y: 0 - width: thumb.client.width - height: thumb.client.height + width: thumb.window.width + height: thumb.window.height } PropertyChanges { target: icon @@ -219,10 +219,10 @@ Item { name: "partial" PropertyChanges { target: thumb - x: (thumb.client.x - targetScreen.geometry.x - (thumb.windowHeap.absolutePositioning ? windowHeap.layout.Kirigami.ScenePosition.x : 0)) * (1 - effect.partialActivationFactor) + cell.x * effect.partialActivationFactor - y: (thumb.client.y - targetScreen.geometry.y - (thumb.windowHeap.absolutePositioning ? windowHeap.layout.Kirigami.ScenePosition.y : 0)) * (1 - effect.partialActivationFactor) + cell.y * effect.partialActivationFactor - width: thumb.client.width * (1 - effect.partialActivationFactor) + cell.width * effect.partialActivationFactor - height: thumb.client.height * (1 - effect.partialActivationFactor) + cell.height * effect.partialActivationFactor + x: (thumb.window.x - targetScreen.geometry.x - (thumb.windowHeap.absolutePositioning ? windowHeap.layout.Kirigami.ScenePosition.x : 0)) * (1 - effect.partialActivationFactor) + cell.x * effect.partialActivationFactor + y: (thumb.window.y - targetScreen.geometry.y - (thumb.windowHeap.absolutePositioning ? windowHeap.layout.Kirigami.ScenePosition.y : 0)) * (1 - effect.partialActivationFactor) + cell.y * effect.partialActivationFactor + width: thumb.window.width * (1 - effect.partialActivationFactor) + cell.width * effect.partialActivationFactor + height: thumb.window.height * (1 - effect.partialActivationFactor) + cell.height * effect.partialActivationFactor opacity: thumb.initialHidden ? (thumb.activeHidden ? 0 : effect.partialActivationFactor) : (thumb.activeHidden ? 1 - effect.partialActivationFactor : 1) @@ -354,7 +354,7 @@ Item { TapHandler { acceptedButtons: Qt.LeftButton onTapped: { - KWinComponents.Workspace.activeClient = thumb.client; + KWinComponents.Workspace.activeClient = thumb.window; thumb.windowHeap.activated(); } onPressedChanged: { @@ -375,7 +375,7 @@ Item { acceptedPointerTypes: PointerDevice.GenericPointer | PointerDevice.Pen acceptedButtons: Qt.LeftButton | Qt.MiddleButton | Qt.RightButton onTapped: { - thumb.windowHeap.windowClicked(thumb.client, eventPoint) + thumb.windowHeap.windowClicked(thumb.window, eventPoint) } } @@ -455,7 +455,7 @@ Item { margins: PlasmaCore.Units.smallSpacing } - visible: thumb.closeButtonVisible && (hoverHandler.hovered || Kirigami.Settings.tabletMode || Kirigami.Settings.hasTransientTouchInput) && thumb.client.closeable && !thumb.activeDragHandler.active + visible: thumb.closeButtonVisible && (hoverHandler.hovered || Kirigami.Settings.tabletMode || Kirigami.Settings.hasTransientTouchInput) && thumb.window.closeable && !thumb.activeDragHandler.active LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft text: i18ndc("kwin_effects", "@info:tooltip as in: 'close this window'", "Close window") @@ -467,7 +467,7 @@ Item { PC3.ToolTip.delay: Kirigami.Units.toolTipDelay Accessible.name: text - onClicked: thumb.client.closeWindow(); + onClicked: thumb.window.closeWindow(); } Component.onDestruction: { diff --git a/src/effects/tileseditor/qml/main.qml b/src/effects/tileseditor/qml/main.qml index a94f67f10e..75167e98f2 100644 --- a/src/effects/tileseditor/qml/main.qml +++ b/src/effects/tileseditor/qml/main.qml @@ -51,21 +51,21 @@ FocusScope { anchors.fill: parent Repeater { - model: KWinComponents.ClientFilterModel { + model: KWinComponents.WindowFilterModel { activity: KWinComponents.Workspace.currentActivity desktop: KWinComponents.Workspace.currentDesktop screenName: targetScreen.name - clientModel: KWinComponents.ClientModel {} + windowModel: KWinComponents.WindowModel {} } KWinComponents.WindowThumbnailItem { - wId: model.client.internalId - x: model.client.x - targetScreen.geometry.x - y: model.client.y - targetScreen.geometry.y - width: model.client.width - height: model.client.height - z: model.client.stackingOrder - visible: !model.client.minimized + wId: model.window.internalId + x: model.window.x - targetScreen.geometry.x + y: model.window.y - targetScreen.geometry.y + width: model.window.width + height: model.window.height + z: model.window.stackingOrder + visible: !model.window.minimized } } property real blurRadius: root.active ? 64 : 0 diff --git a/src/effects/windowview/qml/main.qml b/src/effects/windowview/qml/main.qml index cebc8fa936..caefb04506 100644 --- a/src/effects/windowview/qml/main.qml +++ b/src/effects/windowview/qml/main.qml @@ -186,7 +186,7 @@ Item { } window.closeWindow(); } - model: KWinComponents.ClientFilterModel { + model: KWinComponents.WindowFilterModel { activity: KWinComponents.Workspace.currentActivity desktop: { switch (container.effect.mode) { @@ -198,18 +198,18 @@ Item { } } screenName: targetScreen.name - clientModel: stackModel + windowModel: stackModel filter: effect.searchText minimizedWindows: !effect.ignoreMinimized - windowType: ~KWinComponents.ClientFilterModel.Dock & - ~KWinComponents.ClientFilterModel.Desktop & - ~KWinComponents.ClientFilterModel.Notification & - ~KWinComponents.ClientFilterModel.CriticalNotification + windowType: ~KWinComponents.WindowFilterModel.Dock & + ~KWinComponents.WindowFilterModel.Desktop & + ~KWinComponents.WindowFilterModel.Notification & + ~KWinComponents.WindowFilterModel.CriticalNotification } delegate: WindowHeapDelegate { windowHeap: heap opacity: 1 - downGestureProgress - onDownGestureTriggered: client.closeWindow() + onDownGestureTriggered: window.closeWindow() } onActivated: effect.deactivate(container.effect.animationDuration); } @@ -218,21 +218,21 @@ Item { Instantiator { asynchronous: true - model: KWinComponents.ClientFilterModel { + model: KWinComponents.WindowFilterModel { desktop: KWinComponents.Workspace.currentDesktop screenName: targetScreen.name - clientModel: stackModel - windowType: KWinComponents.ClientFilterModel.Dock + windowModel: stackModel + windowType: KWinComponents.WindowFilterModel.Dock } KWinComponents.WindowThumbnailItem { id: windowThumbnail - wId: model.client.internalId - x: model.client.x - targetScreen.geometry.x - y: model.client.y - targetScreen.geometry.y - z: model.client.stackingOrder + wId: model.window.internalId + x: model.window.x - targetScreen.geometry.x + y: model.window.y - targetScreen.geometry.y + z: model.window.stackingOrder visible: opacity > 0 - opacity: (model.client.hidden || container.organized) ? 0 : 1 + opacity: (model.window.hidden || container.organized) ? 0 : 1 Behavior on opacity { NumberAnimation { duration: container.effect.animationDuration; easing.type: Easing.OutCubic } @@ -244,7 +244,7 @@ Item { } } - KWinComponents.ClientModel { + KWinComponents.WindowModel { id: stackModel } diff --git a/src/scripting/clientmodel.cpp b/src/scripting/clientmodel.cpp deleted file mode 100644 index 3fe2fc1405..0000000000 --- a/src/scripting/clientmodel.cpp +++ /dev/null @@ -1,330 +0,0 @@ -/* - SPDX-FileCopyrightText: 2021 Vlad Zahorodnii - - SPDX-License-Identifier: GPL-2.0-or-later -*/ - -#include "clientmodel.h" -#include "core/output.h" -#include "core/outputbackend.h" -#include "virtualdesktops.h" -#include "window.h" -#include "workspace.h" - -namespace KWin -{ - -ClientModel::ClientModel(QObject *parent) - : QAbstractListModel(parent) -{ - connect(workspace(), &Workspace::windowAdded, this, &ClientModel::handleClientAdded); - connect(workspace(), &Workspace::windowRemoved, this, &ClientModel::handleClientRemoved); - - m_clients = workspace()->allClientList(); - for (Window *client : std::as_const(m_clients)) { - setupClientConnections(client); - } -} - -void ClientModel::markRoleChanged(Window *client, int role) -{ - const QModelIndex row = index(m_clients.indexOf(client), 0); - Q_EMIT dataChanged(row, row, {role}); -} - -void ClientModel::setupClientConnections(Window *client) -{ - connect(client, &Window::desktopChanged, this, [this, client]() { - markRoleChanged(client, DesktopRole); - }); - connect(client, &Window::outputChanged, this, [this, client]() { - markRoleChanged(client, OutputRole); - }); - connect(client, &Window::activitiesChanged, this, [this, client]() { - markRoleChanged(client, ActivityRole); - }); -} - -void ClientModel::handleClientAdded(Window *client) -{ - beginInsertRows(QModelIndex(), m_clients.count(), m_clients.count()); - m_clients.append(client); - endInsertRows(); - - setupClientConnections(client); -} - -void ClientModel::handleClientRemoved(Window *client) -{ - const int index = m_clients.indexOf(client); - Q_ASSERT(index != -1); - - beginRemoveRows(QModelIndex(), index, index); - m_clients.removeAt(index); - endRemoveRows(); -} - -QHash ClientModel::roleNames() const -{ - return { - {Qt::DisplayRole, QByteArrayLiteral("display")}, - {ClientRole, QByteArrayLiteral("client")}, - {OutputRole, QByteArrayLiteral("output")}, - {DesktopRole, QByteArrayLiteral("desktop")}, - {ActivityRole, QByteArrayLiteral("activity")}, - }; -} - -QVariant ClientModel::data(const QModelIndex &index, int role) const -{ - if (!index.isValid() || index.row() < 0 || index.row() >= m_clients.count()) { - return QVariant(); - } - - Window *client = m_clients[index.row()]; - switch (role) { - case Qt::DisplayRole: - case ClientRole: - return QVariant::fromValue(client); - case OutputRole: - return QVariant::fromValue(client->output()); - case DesktopRole: - return client->desktop(); - case ActivityRole: - return client->activities(); - default: - return QVariant(); - } -} - -int ClientModel::rowCount(const QModelIndex &parent) const -{ - return parent.isValid() ? 0 : m_clients.count(); -} - -ClientFilterModel::ClientFilterModel(QObject *parent) - : QSortFilterProxyModel(parent) -{ -} - -ClientModel *ClientFilterModel::clientModel() const -{ - return m_clientModel; -} - -void ClientFilterModel::setClientModel(ClientModel *clientModel) -{ - if (clientModel == m_clientModel) { - return; - } - m_clientModel = clientModel; - setSourceModel(m_clientModel); - Q_EMIT clientModelChanged(); -} - -QString ClientFilterModel::activity() const -{ - return m_activity.value_or(QString()); -} - -void ClientFilterModel::setActivity(const QString &activity) -{ - if (m_activity != activity) { - m_activity = activity; - Q_EMIT activityChanged(); - invalidateFilter(); - } -} - -void ClientFilterModel::resetActivity() -{ - if (m_activity.has_value()) { - m_activity.reset(); - Q_EMIT activityChanged(); - invalidateFilter(); - } -} - -VirtualDesktop *ClientFilterModel::desktop() const -{ - return m_desktop; -} - -void ClientFilterModel::setDesktop(VirtualDesktop *desktop) -{ - if (m_desktop != desktop) { - m_desktop = desktop; - Q_EMIT desktopChanged(); - invalidateFilter(); - } -} - -void ClientFilterModel::resetDesktop() -{ - setDesktop(nullptr); -} - -QString ClientFilterModel::filter() const -{ - return m_filter; -} - -void ClientFilterModel::setFilter(const QString &filter) -{ - if (filter == m_filter) { - return; - } - m_filter = filter; - Q_EMIT filterChanged(); - invalidateFilter(); -} - -QString ClientFilterModel::screenName() const -{ - return m_output ? m_output->name() : QString(); -} - -void ClientFilterModel::setScreenName(const QString &screen) -{ - Output *output = kwinApp()->outputBackend()->findOutput(screen); - if (m_output != output) { - m_output = output; - Q_EMIT screenNameChanged(); - invalidateFilter(); - } -} - -void ClientFilterModel::resetScreenName() -{ - if (m_output) { - m_output = nullptr; - Q_EMIT screenNameChanged(); - invalidateFilter(); - } -} - -ClientFilterModel::WindowTypes ClientFilterModel::windowType() const -{ - return m_windowType.value_or(WindowTypes()); -} - -void ClientFilterModel::setWindowType(WindowTypes windowType) -{ - if (m_windowType != windowType) { - m_windowType = windowType; - Q_EMIT windowTypeChanged(); - invalidateFilter(); - } -} - -void ClientFilterModel::resetWindowType() -{ - if (m_windowType.has_value()) { - m_windowType.reset(); - Q_EMIT windowTypeChanged(); - invalidateFilter(); - } -} - -void ClientFilterModel::setMinimizedWindows(bool show) -{ - if (m_showMinimizedWindows == show) { - return; - } - - m_showMinimizedWindows = show; - invalidateFilter(); - Q_EMIT minimizedWindowsChanged(); -} - -bool ClientFilterModel::minimizedWindows() const -{ - return m_showMinimizedWindows; -} - -bool ClientFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const -{ - if (!m_clientModel) { - return false; - } - const QModelIndex index = m_clientModel->index(sourceRow, 0, sourceParent); - if (!index.isValid()) { - return false; - } - const QVariant data = index.data(); - if (!data.isValid()) { - // an invalid QVariant is valid data - return true; - } - - Window *client = qvariant_cast(data); - if (!client) { - return false; - } - - if (m_activity.has_value()) { - if (!client->isOnActivity(*m_activity)) { - return false; - } - } - - if (m_desktop) { - if (!client->isOnDesktop(m_desktop)) { - return false; - } - } - - if (m_output) { - if (!client->isOnOutput(m_output)) { - return false; - } - } - - if (m_windowType.has_value()) { - if (!(windowTypeMask(client) & *m_windowType)) { - return false; - } - } - - if (!m_filter.isEmpty()) { - if (client->caption().contains(m_filter, Qt::CaseInsensitive)) { - return true; - } - if (client->windowRole().contains(m_filter, Qt::CaseInsensitive)) { - return true; - } - if (client->resourceName().contains(m_filter, Qt::CaseInsensitive)) { - return true; - } - if (client->resourceClass().contains(m_filter, Qt::CaseInsensitive)) { - return true; - } - return false; - } - - if (!m_showMinimizedWindows) { - return !client->isMinimized(); - } - return true; -} - -ClientFilterModel::WindowTypes ClientFilterModel::windowTypeMask(Window *client) const -{ - WindowTypes mask; - if (client->isNormalWindow()) { - mask |= WindowType::Normal; - } else if (client->isDialog()) { - mask |= WindowType::Dialog; - } else if (client->isDock()) { - mask |= WindowType::Dock; - } else if (client->isDesktop()) { - mask |= WindowType::Desktop; - } else if (client->isNotification()) { - mask |= WindowType::Notification; - } else if (client->isCriticalNotification()) { - mask |= WindowType::CriticalNotification; - } - return mask; -} - -} // namespace KWin diff --git a/src/scripting/scripting.cpp b/src/scripting/scripting.cpp index 1973600c76..ef21bf1591 100644 --- a/src/scripting/scripting.cpp +++ b/src/scripting/scripting.cpp @@ -11,7 +11,6 @@ #include "scripting.h" // own -#include "clientmodel.h" #include "dbuscall.h" #include "desktopbackgrounditem.h" #include "kwinquickeffect.h" @@ -19,6 +18,7 @@ #include "scripting_logging.h" #include "scriptingutils.h" #include "virtualdesktopmodel.h" +#include "windowmodel.h" #include "windowthumbnailitem.h" #include "workspace_wrapper.h" @@ -677,8 +677,8 @@ void KWin::Scripting::init() qmlRegisterType("org.kde.kwin", 3, 0, "WindowThumbnailItem"); qmlRegisterType("org.kde.kwin", 3, 0, "DBusCall"); qmlRegisterType("org.kde.kwin", 3, 0, "ScreenEdgeItem"); - qmlRegisterType("org.kde.kwin", 3, 0, "ClientModel"); - qmlRegisterType("org.kde.kwin", 3, 0, "ClientFilterModel"); + qmlRegisterType("org.kde.kwin", 3, 0, "WindowModel"); + qmlRegisterType("org.kde.kwin", 3, 0, "WindowFilterModel"); qmlRegisterType("org.kde.kwin", 3, 0, "VirtualDesktopModel"); qmlRegisterUncreatableType("org.kde.kwin", 3, 0, "SceneView", QStringLiteral("Can't instantiate an object of type SceneView")); diff --git a/src/scripting/windowmodel.cpp b/src/scripting/windowmodel.cpp new file mode 100644 index 0000000000..c706691650 --- /dev/null +++ b/src/scripting/windowmodel.cpp @@ -0,0 +1,330 @@ +/* + SPDX-FileCopyrightText: 2021 Vlad Zahorodnii + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#include "windowmodel.h" +#include "core/output.h" +#include "core/outputbackend.h" +#include "virtualdesktops.h" +#include "window.h" +#include "workspace.h" + +namespace KWin +{ + +WindowModel::WindowModel(QObject *parent) + : QAbstractListModel(parent) +{ + connect(workspace(), &Workspace::windowAdded, this, &WindowModel::handleWindowAdded); + connect(workspace(), &Workspace::windowRemoved, this, &WindowModel::handleWindowRemoved); + + m_windows = workspace()->allClientList(); + for (Window *window : std::as_const(m_windows)) { + setupWindowConnections(window); + } +} + +void WindowModel::markRoleChanged(Window *window, int role) +{ + const QModelIndex row = index(m_windows.indexOf(window), 0); + Q_EMIT dataChanged(row, row, {role}); +} + +void WindowModel::setupWindowConnections(Window *window) +{ + connect(window, &Window::desktopChanged, this, [this, window]() { + markRoleChanged(window, DesktopRole); + }); + connect(window, &Window::outputChanged, this, [this, window]() { + markRoleChanged(window, OutputRole); + }); + connect(window, &Window::activitiesChanged, this, [this, window]() { + markRoleChanged(window, ActivityRole); + }); +} + +void WindowModel::handleWindowAdded(Window *window) +{ + beginInsertRows(QModelIndex(), m_windows.count(), m_windows.count()); + m_windows.append(window); + endInsertRows(); + + setupWindowConnections(window); +} + +void WindowModel::handleWindowRemoved(Window *window) +{ + const int index = m_windows.indexOf(window); + Q_ASSERT(index != -1); + + beginRemoveRows(QModelIndex(), index, index); + m_windows.removeAt(index); + endRemoveRows(); +} + +QHash WindowModel::roleNames() const +{ + return { + {Qt::DisplayRole, QByteArrayLiteral("display")}, + {WindowRole, QByteArrayLiteral("window")}, + {OutputRole, QByteArrayLiteral("output")}, + {DesktopRole, QByteArrayLiteral("desktop")}, + {ActivityRole, QByteArrayLiteral("activity")}, + }; +} + +QVariant WindowModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || index.row() < 0 || index.row() >= m_windows.count()) { + return QVariant(); + } + + Window *window = m_windows[index.row()]; + switch (role) { + case Qt::DisplayRole: + case WindowRole: + return QVariant::fromValue(window); + case OutputRole: + return QVariant::fromValue(window->output()); + case DesktopRole: + return window->desktop(); + case ActivityRole: + return window->activities(); + default: + return QVariant(); + } +} + +int WindowModel::rowCount(const QModelIndex &parent) const +{ + return parent.isValid() ? 0 : m_windows.count(); +} + +WindowFilterModel::WindowFilterModel(QObject *parent) + : QSortFilterProxyModel(parent) +{ +} + +WindowModel *WindowFilterModel::windowModel() const +{ + return m_windowModel; +} + +void WindowFilterModel::setWindowModel(WindowModel *windowModel) +{ + if (windowModel == m_windowModel) { + return; + } + m_windowModel = windowModel; + setSourceModel(m_windowModel); + Q_EMIT windowModelChanged(); +} + +QString WindowFilterModel::activity() const +{ + return m_activity.value_or(QString()); +} + +void WindowFilterModel::setActivity(const QString &activity) +{ + if (m_activity != activity) { + m_activity = activity; + Q_EMIT activityChanged(); + invalidateFilter(); + } +} + +void WindowFilterModel::resetActivity() +{ + if (m_activity.has_value()) { + m_activity.reset(); + Q_EMIT activityChanged(); + invalidateFilter(); + } +} + +VirtualDesktop *WindowFilterModel::desktop() const +{ + return m_desktop; +} + +void WindowFilterModel::setDesktop(VirtualDesktop *desktop) +{ + if (m_desktop != desktop) { + m_desktop = desktop; + Q_EMIT desktopChanged(); + invalidateFilter(); + } +} + +void WindowFilterModel::resetDesktop() +{ + setDesktop(nullptr); +} + +QString WindowFilterModel::filter() const +{ + return m_filter; +} + +void WindowFilterModel::setFilter(const QString &filter) +{ + if (filter == m_filter) { + return; + } + m_filter = filter; + Q_EMIT filterChanged(); + invalidateFilter(); +} + +QString WindowFilterModel::screenName() const +{ + return m_output ? m_output->name() : QString(); +} + +void WindowFilterModel::setScreenName(const QString &screen) +{ + Output *output = kwinApp()->outputBackend()->findOutput(screen); + if (m_output != output) { + m_output = output; + Q_EMIT screenNameChanged(); + invalidateFilter(); + } +} + +void WindowFilterModel::resetScreenName() +{ + if (m_output) { + m_output = nullptr; + Q_EMIT screenNameChanged(); + invalidateFilter(); + } +} + +WindowFilterModel::WindowTypes WindowFilterModel::windowType() const +{ + return m_windowType.value_or(WindowTypes()); +} + +void WindowFilterModel::setWindowType(WindowTypes windowType) +{ + if (m_windowType != windowType) { + m_windowType = windowType; + Q_EMIT windowTypeChanged(); + invalidateFilter(); + } +} + +void WindowFilterModel::resetWindowType() +{ + if (m_windowType.has_value()) { + m_windowType.reset(); + Q_EMIT windowTypeChanged(); + invalidateFilter(); + } +} + +void WindowFilterModel::setMinimizedWindows(bool show) +{ + if (m_showMinimizedWindows == show) { + return; + } + + m_showMinimizedWindows = show; + invalidateFilter(); + Q_EMIT minimizedWindowsChanged(); +} + +bool WindowFilterModel::minimizedWindows() const +{ + return m_showMinimizedWindows; +} + +bool WindowFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const +{ + if (!m_windowModel) { + return false; + } + const QModelIndex index = m_windowModel->index(sourceRow, 0, sourceParent); + if (!index.isValid()) { + return false; + } + const QVariant data = index.data(); + if (!data.isValid()) { + // an invalid QVariant is valid data + return true; + } + + Window *window = qvariant_cast(data); + if (!window) { + return false; + } + + if (m_activity.has_value()) { + if (!window->isOnActivity(*m_activity)) { + return false; + } + } + + if (m_desktop) { + if (!window->isOnDesktop(m_desktop)) { + return false; + } + } + + if (m_output) { + if (!window->isOnOutput(m_output)) { + return false; + } + } + + if (m_windowType.has_value()) { + if (!(windowTypeMask(window) & *m_windowType)) { + return false; + } + } + + if (!m_filter.isEmpty()) { + if (window->caption().contains(m_filter, Qt::CaseInsensitive)) { + return true; + } + if (window->windowRole().contains(m_filter, Qt::CaseInsensitive)) { + return true; + } + if (window->resourceName().contains(m_filter, Qt::CaseInsensitive)) { + return true; + } + if (window->resourceClass().contains(m_filter, Qt::CaseInsensitive)) { + return true; + } + return false; + } + + if (!m_showMinimizedWindows) { + return !window->isMinimized(); + } + return true; +} + +WindowFilterModel::WindowTypes WindowFilterModel::windowTypeMask(Window *window) const +{ + WindowTypes mask; + if (window->isNormalWindow()) { + mask |= WindowType::Normal; + } else if (window->isDialog()) { + mask |= WindowType::Dialog; + } else if (window->isDock()) { + mask |= WindowType::Dock; + } else if (window->isDesktop()) { + mask |= WindowType::Desktop; + } else if (window->isNotification()) { + mask |= WindowType::Notification; + } else if (window->isCriticalNotification()) { + mask |= WindowType::CriticalNotification; + } + return mask; +} + +} // namespace KWin diff --git a/src/scripting/clientmodel.h b/src/scripting/windowmodel.h similarity index 77% rename from src/scripting/clientmodel.h rename to src/scripting/windowmodel.h index fb826b47f7..98db947c40 100644 --- a/src/scripting/clientmodel.h +++ b/src/scripting/windowmodel.h @@ -19,38 +19,38 @@ namespace KWin class Window; class Output; -class ClientModel : public QAbstractListModel +class WindowModel : public QAbstractListModel { Q_OBJECT public: enum Roles { - ClientRole = Qt::UserRole + 1, + WindowRole = Qt::UserRole + 1, OutputRole, DesktopRole, ActivityRole }; - explicit ClientModel(QObject *parent = nullptr); + explicit WindowModel(QObject *parent = nullptr); QHash roleNames() const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; private: - void markRoleChanged(Window *client, int role); + void markRoleChanged(Window *window, int role); - void handleClientAdded(Window *client); - void handleClientRemoved(Window *client); - void setupClientConnections(Window *client); + void handleWindowAdded(Window *window); + void handleWindowRemoved(Window *window); + void setupWindowConnections(Window *window); - QList m_clients; + QList m_windows; }; -class ClientFilterModel : public QSortFilterProxyModel +class WindowFilterModel : public QSortFilterProxyModel { Q_OBJECT - Q_PROPERTY(ClientModel *clientModel READ clientModel WRITE setClientModel NOTIFY clientModelChanged) + Q_PROPERTY(WindowModel *windowModel READ windowModel WRITE setWindowModel NOTIFY windowModelChanged) Q_PROPERTY(QString activity READ activity WRITE setActivity RESET resetActivity NOTIFY activityChanged) Q_PROPERTY(KWin::VirtualDesktop *desktop READ desktop WRITE setDesktop RESET resetDesktop NOTIFY desktopChanged) Q_PROPERTY(QString filter READ filter WRITE setFilter NOTIFY filterChanged) @@ -70,10 +70,10 @@ public: Q_DECLARE_FLAGS(WindowTypes, WindowType) Q_FLAG(WindowTypes) - explicit ClientFilterModel(QObject *parent = nullptr); + explicit WindowFilterModel(QObject *parent = nullptr); - ClientModel *clientModel() const; - void setClientModel(ClientModel *clientModel); + WindowModel *windowModel() const; + void setWindowModel(WindowModel *windowModel); QString activity() const; void setActivity(const QString &activity); @@ -104,15 +104,15 @@ Q_SIGNALS: void activityChanged(); void desktopChanged(); void screenNameChanged(); - void clientModelChanged(); + void windowModelChanged(); void filterChanged(); void windowTypeChanged(); void minimizedWindowsChanged(); private: - WindowTypes windowTypeMask(Window *client) const; + WindowTypes windowTypeMask(Window *window) const; - ClientModel *m_clientModel = nullptr; + WindowModel *m_windowModel = nullptr; std::optional m_activity; QPointer m_output; QPointer m_desktop;